diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 00000000..dd234131 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,141 @@ +# Copilot Instructions for httpSMS + +httpSMS is a service that turns an Android phone into an SMS gateway via an HTTP API. This is a monorepo with three components: + +- **`api/`** — Go backend (Fiber, GORM, PostgreSQL) +- **`web/`** — Nuxt 2 frontend (Vue 2, Vuetify 2, TypeScript) +- **`android/`** — Native Android app (Kotlin) + +## Build, Test, and Lint Commands + +### API (Go) + +```bash +cd api + +# Development with hot-reload +air + +# Build +go build -o ./tmp/main.exe . + +# Run tests +go test ./... + +# Run a single test +go test ./pkg/services/ -run TestMessageService + +# Generate Swagger docs (required after changing API annotations) +swag init --requiredByDefault --parseDependency --parseInternal + +# Pre-commit hooks run: go-fumpt, go-imports, go-lint, go-mod-tidy +``` + +### Web (Nuxt/Vue) + +```bash +cd web + +# Install dependencies +pnpm install + +# Development server (port 3000) +pnpm dev + +# Lint (eslint + stylelint + prettier) +pnpm lint + +# Auto-fix lint issues +pnpm lintfix + +# Run tests (Jest) +pnpm test + +# Static site generation (production build) +pnpm run generate + +# Regenerate TypeScript API models from Swagger +pnpm api:models +``` + +### Android (Kotlin) + +```bash +cd android + +# Build +./gradlew build + +# Debug APK +./gradlew assembleDebug + +# Release APK +./gradlew assembleRelease +``` + +### Docker (full stack) + +```bash +# Start all services (PostgreSQL, Redis, API, Web) +docker compose up --build +# API at localhost:8000, Web at localhost:3000 +``` + +## Architecture + +### API — Layered Architecture with Event-Driven Processing + +The API uses a **DI container** (`pkg/di/container.go`) that lazily initializes all services as singletons. The layered architecture flows as: + +**Handlers → Services → Repositories → GORM/PostgreSQL** + +- **Handlers** (`pkg/handlers/`) — Fiber HTTP handlers. Each has a `RegisterRoutes()` method and embeds a base `handler` struct with standardized response methods (`responseBadRequest`, `responseNotFound`, etc.). +- **Services** (`pkg/services/`) — Business logic. Orchestrate repositories and dispatch events. +- **Repositories** (`pkg/repositories/`) — Data access via GORM. Interfaces defined alongside GORM implementations (prefixed `gorm*`). +- **Validators** (`pkg/validators/`) — One validator per handler, return `url.Values` for field errors. +- **Entities** (`pkg/entities/`) — Domain models, auto-migrated by GORM. + +**Event system**: Uses CloudEvents spec (`cloudevents/sdk-go`). Events defined in `pkg/events/` (31 event types). Listeners in `pkg/listeners/` process events either synchronously or via Google Cloud Tasks queue (emulator mode for local dev). + +**Entry point**: `main.go` loads `.env` in local mode, creates the DI container, and starts Fiber on `APP_PORT`. + +### Web — Nuxt 2 Static SPA + +- **State management**: Single Vuex store (`store/index.ts`) — actions make API calls via Axios, mutations update state, getters expose computed values. +- **Components**: Use `vue-property-decorator` class syntax with `@Component`, `@Prop`, `@Watch` decorators. +- **API client**: Axios configured in `plugins/axios.ts` with Firebase bearer token auth and `x-api-key` header support. +- **API models**: TypeScript types in `models/` are auto-generated from the Swagger spec via `swagger-typescript-api`. +- **Auth**: Firebase Authentication (Email/Password, Google, GitHub) with `auth` and `guest` middleware for route guards. +- **Real-time**: Pusher.js for live message updates. + +### Android — Task-Oriented, Event-Driven + +- **No MVVM/Clean Architecture** — uses a flat package structure with Activities, Services, BroadcastReceivers, and WorkManager tasks. +- **FCM integration**: `MyFirebaseMessagingService` receives push notifications → schedules `SendSmsWorker` via WorkManager → fetches message from API → sends SMS. +- **Dual SIM support**: Independent settings per SIM via `Settings` singleton (SharedPreferences). +- **HTTP client**: OkHttp with `x-api-key` authentication against the API. +- **Encryption**: AES-256/CFB with SHA-256 key derivation (`Encrypter.kt`). + +## Key Conventions + +### API (Go) + +- **Error handling**: Use `github.com/palantir/stacktrace` — wrap errors with `stacktrace.Propagate(err, "context")` or `stacktrace.PropagateWithCode()`. Never return bare errors. +- **Database queries**: Always use GORM query builder with context propagation (`repository.db.WithContext(ctx)`). No raw SQL. +- **Route registration**: Each handler defines `RegisterRoutes()` called from the DI container. Routes follow REST conventions under `/v1/`. +- **Middleware chain**: HTTP Logger → OpenTelemetry → CORS → Request Logger → Bearer Auth → API Key Auth. +- **Observability**: All layers are instrumented with OpenTelemetry (Fiber, GORM, Redis). Pass `logger` and `tracer` to constructors. +- **Code formatting**: `go-fumpt` (not `gofmt`), enforced via pre-commit hooks. + +### Web (Vue/TypeScript) + +- **Formatting**: No semicolons, single quotes, 2-space indentation (Prettier + ESLint). +- **Component style**: Class-based with `vue-property-decorator`, not Options API (though some pages use `Vue.extend()`). +- **Store pattern**: Actions handle async API calls and commit mutations. Access store from components via `this.$store`. + +### Android (Kotlin) + +- **API calls**: Use `HttpSmsApiService` singleton (static `create()` factory). OkHttp client with `x-api-key` header. +- **Background work**: Use WorkManager for tasks that must survive process death. Direct `Thread { }` for lightweight background ops. +- **State**: `Settings` object (SharedPreferences singleton) for all persistent state. +- **Phone number formatting**: Use `libphonenumber` for E.164 format validation. diff --git a/.github/ghbadge.png b/.github/ghbadge.png new file mode 100644 index 00000000..326d2547 Binary files /dev/null and b/.github/ghbadge.png differ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5a581dab..27bcd737 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,44 +16,25 @@ jobs: strategy: matrix: os: [ubuntu-latest] - node: [16] + node: [20] steps: - name: Checkout 🛎 uses: actions/checkout@master - - name: Install Node.js - uses: actions/setup-node@v4 - with: - node-version: 16 - - - uses: pnpm/action-setup@v2 + - uses: pnpm/action-setup@v5 name: Install pnpm with: - version: 8 - run_install: false - - - name: Get pnpm store directory - shell: bash - run: | - echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV - - - uses: actions/cache@v3 - name: Setup pnpm cache - with: - path: ${{ env.STORE_PATH }} - key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} - restore-keys: | - ${{ runner.os }}-pnpm-store- + version: 9 - name: Install dependencies 📦 run: pnpm install - name: Run linter 👀 - run: yarn lint + run: pnpm lint - name: Run tests 🧪 - run: yarn test + run: pnpm test - name: Debug 🐛 run: echo GITHUB_SHA=${GITHUB_SHA} @@ -61,7 +42,16 @@ jobs: - name: Build 🏗️ run: mv .env.production .env && echo GITHUB_SHA=${GITHUB_SHA} >> .env && pnpm run generate - - name: Deploy 🚀 + - name: Cloudflare Deploy 🚀 + uses: cloudflare/pages-action@1 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + projectName: httpsms + directory: web/dist + gitHubToken: ${{ secrets.GITHUB_TOKEN }} + + - name: Firebase Deploy 🚀 uses: FirebaseExtended/action-hosting-deploy@v0 with: repoToken: "${{ secrets.GITHUB_TOKEN }}" diff --git a/.gitignore b/.gitignore index 94898cad..b114cdd0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ .idea -docs .env* +!.env.docker +!.env.production *serviceAccountKey.json android/app/debug/ *main.exe* diff --git a/.mcp.json b/.mcp.json new file mode 100644 index 00000000..1bb33a71 --- /dev/null +++ b/.mcp.json @@ -0,0 +1,22 @@ +{ + "mcpServers": { + "playwright": { + "type": "stdio", + "command": "npx", + "args": [ + "-y", + "@modelcontextprotocol/server-playwright", + "--base-url", + "http://localhost:3000" + ], + "env": { + "BROWSER": "chromium" + } + }, + "context7": { + "type": "stdio", + "command": "npx", + "args": ["@upstash/context7-mcp@latest"] + } + } +} diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..a538c777 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,131 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, caste, color, religion, or sexual +identity and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +- Demonstrating empathy and kindness toward other people +- Being respectful of differing opinions, viewpoints, and experiences +- Giving and gracefully accepting constructive feedback +- Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +- Focusing on what is best not just for us as individuals, but for the overall + community + +Examples of unacceptable behavior include: + +- The use of sexualized language or imagery, and sexual attention or advances of + any kind +- Trolling, insulting or derogatory comments, and personal or political attacks +- Public or private harassment +- Publishing others' private information, such as a physical or email address, + without their explicit permission +- Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official email address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at arnold@httpsms.com. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series of +actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or permanent +ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within the +community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.1, available at +[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. + +Community Impact Guidelines were inspired by +[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. + +For answers to common questions about this code of conduct, see the FAQ at +[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at +[https://www.contributor-covenant.org/translations][translations]. + +[homepage]: https://www.contributor-covenant.org +[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html +[Mozilla CoC]: https://github.com/mozilla/diversity +[FAQ]: https://www.contributor-covenant.org/faq +[translations]: https://www.contributor-covenant.org/translations diff --git a/README.md b/README.md index 22efa966..84b77a40 100644 --- a/README.md +++ b/README.md @@ -1,30 +1,64 @@ -# HTTP SMS +# httpSMS [![Build](https://github.com/NdoleStudio/httpsms/actions/workflows/ci.yml/badge.svg)](https://github.com/NdoleStudio/httpsms/actions/workflows/ci.yml) [![GitHub contributors](https://img.shields.io/github/contributors/NdoleStudio/httpsms)](https://github.com/NdoleStudio/httpsms/graphs/contributors) [![GitHub license](https://img.shields.io/github/license/NdoleStudio/httpsms?color=brightgreen)](https://github.com/NdoleStudio/httpsms/blob/master/LICENSE) +[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](CODE_OF_CONDUCT.md) +[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/NdoleStudio/httpsms/badges/quality-score.png?b=main)](https://scrutinizer-ci.com/g/NdoleStudio/httpsms/?branch=main) +[![Better Stack Badge](https://uptime.betterstack.com/status-badges/v1/monitor/ldl5.svg)](https://uptime.betterstack.com/?utm_source=status_badge) +[![Sponsors](https://img.shields.io/github/sponsors/ndolestudio?logo=github)](https://github.com/sponsors/ndolestudio) +[![Discord](https://img.shields.io/discord/1095778291488653372?label=Discord)](https://discord.gg/kGk8HVqeEZ) -[HTTP SMS](https://httpsms.com) is a service that lets you use your android phone as an SMS Gateway to send and receive SMS messages. -You make a request to the API which it triggers your android phone to send an SMS. +[httpSMS](https://httpsms.com) is a service that lets you use your Android phone as an SMS Gateway to send and receive SMS messages. +You make a request to a simple HTTP API and it triggers your Android phone to send an SMS. SMS messages received on your android phone can also be forwarded to your webhook endpoint. Quick Start Guide 👉 [https://docs.httpsms.com](https://docs.httpsms.com) header +## Table Of Contents + + + + +- [Why?](#why) +- [Web UI](#web-ui) +- [API](#api) +- [Android App](#android-app) +- [Chat/forum](#chatforum) +- [Features](#features) + - [End-to-end Encryption](#end-to-end-encryption) + - [Webhook](#webhook) + - [Back Pressure](#back-pressure) + - [Message Expiration](#message-expiration) +- [API Clients](#api-clients) +- [Flows](#flows) + - [Sending an SMS Message](#sending-an-sms-message) +- [Self Host Setup - Docker](#self-host-setup---docker) + - [1. Setup Firebase](#1-setup-firebase) + - [2. Setup SMTP Email service](#2-setup-smtp-email-service) + - [3. Setup Cloudflare Turnstile](#3-setup-cloudflare-turnstile) + - [4. Download the code](#4-download-the-code) + - [5. Setup the environment variables](#5-setup-the-environment-variables) + - [6. Build and Run](#6-build-and-run) + - [7. Create the System User](#7-create-the-system-user) + - [8. Build the Android App.](#8-build-the-android-app) +- [License](#license) + + + ## Why? I'm originally from Cameroon and I wanted an automated way to send and receive SMS messages using an API. Unfortunately many countries don't support the ability to buy virtual phone numbers, and I could not find a good ready-made solution that could help me send/receive SMS messages using a mobile phone using an intuitive http API. -## Technology - -### Web +## Web UI The web interface https://httpsms.com is built using [Nuxt](https://nuxtjs.org/) and [Vuetify](https://vuetifyjs.com/en/). It is hosted as a single page application on firebase. The source code is in the [web](./web) directory -### API +## API The API https://api.httpsms.com is built using [Fiber](https://gofiber.io/), Go and [CockroachDB](https://www.cockroachlabs.com/) for the database. It rus as a serverless application on Google Cloud Run. The API documentation can be found here https://api.httpsms.com/index.html @@ -40,14 +74,29 @@ client.Messages.Send(context.Background(), &httpsms.MessageSendParams{ }) ``` -### Android App +## Android App + +[The Android App](https://apk.httpsms.com/HttpSms.apk) is a native application built using Kotlin with material design principles. +This app must be installed on an Android phone before you can start sending and receiving SMS messages. + +[Get it on GitHub](https://github.com/NdoleStudio/httpsms/releases/) -[The Android App](https://github.com/NdoleStudio/httpsms/releases/latest/download/HttpSms.apk) is a native application -built using Kotlin with material design principles. This app must be installed on an android phone before you can start -sending and receiving SMS messages. +## Chat/forum + +There are a few ways to get in touch with me and/or the rest of the community. Feel free to use any of these methods. Whatever +works best for you: + +- [Discord server](https://discord.gg/kGk8HVqeEZ) - direct chat with the community +- [GitHub issues](https://github.com/NdoleStudio/httpsms/issues) - questions, features, bugs ## Features +### End-to-end Encryption + +You can encrypt your messages end-to-end ysubg the military grade [AES-256 encryption](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard) +algorithm. Your encryption key is stored only on our mobile phone so the even the server won't have any way to view the +content of your SMS messages which are sent and received on your Android phone. + ### Webhook If you want to build advanced integrations, we support webhooks. The httpSMS platform can forward SMS messages received @@ -66,7 +115,8 @@ will be notified. ## API Clients -- Go: https://github.com/NdoleStudio/httpsms-go +- [x] Go: https://github.com/NdoleStudio/httpsms-go +- [x] JavaScript/TypeScript: https://github.com/NdoleStudio/httpsms-node ## Flows @@ -74,17 +124,137 @@ will be notified. ```mermaid sequenceDiagram -User->>+Http Sms API: Call /v1/messages/send API -Http Sms API-->>+Google Cloud Task: Schedule notification about new message -Http Sms API-->>-User: Respond with 202 (Accepted) -Google Cloud Task-->>+Http Sms API: [Async] Send notfication request -Http Sms API-->>-Android App: Send push notification about new message -Android App-->>Http Sms API: [Async] Fetch message -Android App-->>Android App: Send Message using Andoid SMS API -Android App-->>Http Sms API: [Async] Send result of sending SMS -Android App-->>Http Sms API: [Async] Send Delivery Report +User->>+httpSMS API: Call /v1/messages/send API +httpSMS API-->>+Push Queue: Schedule notification about new message +httpSMS API-->>-User: Respond with 202 (Accepted) +Push Queue-->>+httpSMS API: [Async] Send notification request +httpSMS API-->>-Android App: Send push notification about new message +Android App-->>httpSMS API: [Async] Fetch message +Android App-->>Android App: Send Message using Android SMS API +Android App-->>httpSMS API: [Async] Send result of sending SMS +Android App-->>httpSMS API: [Async] Send Delivery Report +``` + +## Self Host Setup - Docker + +### 1. Setup Firebase + +- The httpSMS application uses [firebase cloud messaging](https://firebase.google.com/docs/cloud-messaging) for sending push notifications to your Android phone to trigger an SMS message to be sent out. + Visit the [firebase console](https://console.firebase.google.com/) and create a new project and follow the [steps here](https://firebase.google.com/docs/web/setup#register-app) to get your firebase web SDK config credentials. + For example, I created a firebase project called `httpsms-docker` and this is my web SDK configuration + +```js +const firebaseConfig = { + apiKey: "AIzaSyAKqPvj51igvvNNcRtxxxxx", + authDomain: "httpsms-docker.firebaseapp.com", + projectId: "httpsms-docker", + storageBucket: "httpsms-docker.appspot.com", + messagingSenderId: "668063041624", + appId: "1:668063041624:web:29b9e3b702796xxxx", + measurementId: "G-18VRYL2xxxx", +}; +``` + +- Enable `Email/Password` sign-in in the [Firebase console](https://console.firebase.google.com/u/0/), open the **Authentication** section. On the Sign in method tab, enable the `Email/password` sign-in method and click `Save`. + - The firebase `email/password` sign-in method has [a bug](https://github.com/firebase/firebaseui-web/issues/1040) which prevents you from signing in. The work around right now is to [disable email enumeration protection](https://cloud.google.com/identity-platform/docs/admin/email-enumeration-protection#disable) on the firebase console. +- Generate your firebase service account credentials by following the [steps here](https://firebase.google.com/docs/admin/setup#initialize_the_sdk_in_non-google_environments) and save the credentials in a file called `firebase-credentials.json` we will use this file to authenticate with the firebase admin SDK. +- Generate your Android `google-services.json` file using [the instructions here](https://support.google.com/firebase/answer/7015592?hl=en#android&zippy=%2Cin-this-article) we will use it letter to configure the android app. + +### 2. Setup SMTP Email service + +The httpSMS application uses [SMTP](https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol) to send emails to users e.g. when your Android phone has been offline for a long period of time. +You can use a service like [mailtrap](https://mailtrap.io/) to create an SMTP server for development purposes. + +### 3. Setup Cloudflare Turnstile + +The message search route (`/v1/messages/search`) is protected by a [Cloudflare Turnstile](https://developers.cloudflare.com/turnstile/get-started/) captcha to prevent abuse. You need to set up a Turnstile widget for the search messages feature to work. + +1. Go to the [Cloudflare dashboard](https://dash.cloudflare.com/) and navigate to **Turnstile**. +2. Add a new site and configure it for your self-hosted domain (e.g., `localhost` for local development). +3. Note down the **Site Key** and **Secret Key** — you will need them for the frontend and backend environment variables respectively. + +### 4. Download the code + +Clone the httpSMS GitHub repository + +```bash +git clone https://github.com/NdoleStudio/httpsms.git +``` + +### 5. Setup the environment variables + +- Copy the `.env.docker` file in the `web` directory into `.env` + +```bash +cp web/.env.docker web/.env +``` + +- Update the environment variables in the `.env` file in the `web` directory with your firebase web SDK configuration in step 1 above + +```dotenv +FIREBASE_API_KEY= +FIREBASE_AUTH_DOMAIN= +FIREBASE_PROJECT_ID= +FIREBASE_STORAGE_BUCKET= +FIREBASE_MESSAGING_SENDER_ID= +FIREBASE_APP_ID= +FIREBASE_MEASUREMENT_ID= + +# Cloudflare Turnstile site key from step 3 +CLOUDFLARE_TURNSTILE_SITE_KEY= ``` +- Copy the `.env.docker` file in the `api` directory into `.env` + +```bash +cp api/.env.docker api/.env +``` + +- Update the environment variables in the `.env` file in the `api` directory with your firebase service account credentials, SMTP server details, and Cloudflare Turnstile secret key. + +```dotenv +# SMTP email server settings +SMTP_USERNAME= +SMTP_PASSWORD= +SMTP_HOST= +SMTP_PORT= + +# Firebase service account credentials +FIREBASE_CREDENTIALS= + +# This is the `projectId` from your firebase web config +GCP_PROJECT_ID= + +# Cloudflare Turnstile secret key from step 3 +CLOUDFLARE_TURNSTILE_SECRET_KEY= +``` + +- Don't bother about the `EVENTS_QUEUE_USER_API_KEY` and `EVENTS_QUEUE_USER_ID` settings. We will set that up later. + +### 6. Build and Run + +- Build and run the API, the web UI, database and cache using the `docker-compose.yml` file. It takes a while for build and download all the docker images. + When it's finished, you'll be able to access the web UI at http://localhost:3000 and the API at http://localhost:8000 + +```bash +docker compose up --build +``` + +### 7. Create the System User + +- The application uses the concept of a system user to process events async. You should manually create this user in `users` table in your database. Make sure you use the same `id` and `api_key` as the `EVENTS_QUEUE_USER_ID`, and `EVENTS_QUEUE_USER_API_KEY` in your `.env` file. + + ```SQL + INSERT INTO users (id, api_key, email ) VALUES ('your-system-user-id', 'your-system-api-key', 'system@domain.com'); + ``` + +> [!IMPORTANT] +> Restart your API docker container after modifying `EVENTS_QUEUE_USER_ID`, and `EVENTS_QUEUE_USER_API_KEY` in your `.env` file so that the httpSMS API can pick up the changes. + +### 8. Build the Android App. + +- Before building the Android app in [Android Studio](https://developer.android.com/studio), you need to replace the `google-services.json` file in the `android/app` directory with the file which you got from step 1. You need to do this for the firebase FCM messages to work properly. + ## License This project is licensed under the GNU AFFERO GENERAL PUBLIC LICENSE Version 3 - see the [LICENSE](LICENSE) file for details diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..35c692f8 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,13 @@ +# Security Policy + +## Supported Versions + +As of today, I only support the latest version of httpSMS in the `main` branch of github. Please make sure you stay up-to-date. + +| Version | Supported | +| ------- | ------------------ | +| main | :white_check_mark: | + +## Reporting a Vulnerability + +Please report severe security issues privately via arnold@httpsms.com or by sending me a private message on [Discord](https://discord.gg/kGk8HVqeEZ). diff --git a/android/app/build.gradle b/android/app/build.gradle deleted file mode 100644 index 416e01d2..00000000 --- a/android/app/build.gradle +++ /dev/null @@ -1,68 +0,0 @@ -plugins { - id 'com.android.application' - id 'org.jetbrains.kotlin.android' - id 'com.google.gms.google-services' - id "io.sentry.android.gradle" version "3.1.2" -} - -def getGitHash = { -> - def stdout = new ByteArrayOutputStream() - exec { - commandLine 'git', 'rev-parse', '--short', 'HEAD' - standardOutput = stdout - } - return stdout.toString().trim() -} - -android { - compileSdk 34 - - defaultConfig { - applicationId "com.httpsms" - minSdk 28 - targetSdk 33 - versionCode 1 - versionName "${getGitHash()}" - testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" - } - - buildTypes { - debug { - manifestPlaceholders["sentryEnvironment"] = "development" - } - release { - manifestPlaceholders["sentryEnvironment"] = "production" - minifyEnabled false - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - kotlinOptions { - jvmTarget = '1.8' - } - namespace 'com.httpsms' -} - -dependencies { - implementation platform('com.google.firebase:firebase-bom:30.1.0') - implementation 'com.google.firebase:firebase-analytics-ktx' - implementation 'com.google.firebase:firebase-messaging-ktx' - implementation 'com.squareup.okhttp3:okhttp:4.10.0' - implementation 'com.jakewharton.timber:timber:5.0.1' - implementation 'androidx.preference:preference-ktx:1.2.1' - implementation 'androidx.work:work-runtime-ktx:2.7.1' - implementation 'androidx.core:core-ktx:1.9.0' - implementation "androidx.cardview:cardview:1.0.0" - implementation 'com.beust:klaxon:5.5' - implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'org.apache.commons:commons-text:1.9' - implementation 'com.google.android.material:material:1.9.0' - implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - implementation 'androidx.core:core-ktx:1.12.0' - testImplementation 'junit:junit:4.13.2' - androidTestImplementation 'androidx.test.ext:junit:1.1.5' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' -} diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts new file mode 100644 index 00000000..15857e70 --- /dev/null +++ b/android/app/build.gradle.kts @@ -0,0 +1,65 @@ +plugins { + id("com.android.application") + id("com.google.gms.google-services") + id("io.sentry.android.gradle") version "6.2.0" +} + +val gitHash = providers.exec { + commandLine("git", "rev-parse", "--short", "HEAD") +}.standardOutput.asText.map { it.trim() } + +android { + compileSdk = 36 + + defaultConfig { + applicationId = "com.httpsms" + minSdk = 28 + targetSdk = 36 + versionCode = 1 + versionName = gitHash.getOrElse("unknown") + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + getByName("debug") { + manifestPlaceholders["sentryEnvironment"] = "development" + } + getByName("release") { + manifestPlaceholders["sentryEnvironment"] = "production" + isMinifyEnabled = false + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + namespace = "com.httpsms" + + buildFeatures { + buildConfig = true + } +} + +dependencies { + implementation(platform("com.google.firebase:firebase-bom:34.11.0")) + implementation("com.journeyapps:zxing-android-embedded:4.3.0") + implementation("com.google.firebase:firebase-analytics") + implementation("com.google.firebase:firebase-messaging") + implementation("com.squareup.okhttp3:okhttp:5.3.2") + implementation("com.jakewharton.timber:timber:5.0.1") + implementation("androidx.preference:preference-ktx:1.2.1") + implementation("androidx.work:work-runtime-ktx:2.11.1") + implementation("androidx.core:core-ktx:1.18.0") + implementation("androidx.cardview:cardview:1.0.0") + implementation("com.beust:klaxon:5.6") + implementation("androidx.appcompat:appcompat:1.7.1") + implementation("org.apache.commons:commons-text:1.15.0") + implementation("com.google.android.material:material:1.13.0") + implementation("androidx.constraintlayout:constraintlayout:2.2.1") + implementation("com.googlecode.libphonenumber:libphonenumber:9.0.26") + implementation("com.klinkerapps:android-smsmms:5.2.6") + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.3.0") + androidTestImplementation("androidx.test.espresso:espresso-core:3.7.0") +} diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 8a076fec..86ca0a51 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -8,9 +8,11 @@ android:required="false" /> + + @@ -18,6 +20,7 @@ + + tools:targetApi="36"> + @@ -64,6 +74,16 @@ + + + + + + + + + + + + + + + diff --git a/android/app/src/main/ic_launcher-playstore.png b/android/app/src/main/ic_launcher-playstore.png index c3049849..e6dc20ce 100644 Binary files a/android/app/src/main/ic_launcher-playstore.png and b/android/app/src/main/ic_launcher-playstore.png differ diff --git a/android/app/src/main/java/com/httpsms/Constants.kt b/android/app/src/main/java/com/httpsms/Constants.kt index 2e202caa..ba3e1584 100644 --- a/android/app/src/main/java/com/httpsms/Constants.kt +++ b/android/app/src/main/java/com/httpsms/Constants.kt @@ -9,6 +9,8 @@ class Constants { const val KEY_MESSAGE_CONTENT = "KEY_MESSAGE_CONTENT" const val KEY_MESSAGE_TIMESTAMP = "KEY_MESSAGE_TIMESTAMP" const val KEY_MESSAGE_REASON = "KEY_MESSAGE_REASON" + const val KEY_MESSAGE_ENCRYPTED = "KEY_MESSAGE_ENCRYPTED" + const val KEY_MESSAGE_ATTACHMENTS = "KEY_MESSAGE_ATTACHMENTS" const val KEY_HEARTBEAT_ID = "KEY_HEARTBEAT_ID" @@ -17,5 +19,7 @@ class Constants { const val SIM2 = "SIM2" const val TIMESTAMP_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'000000'ZZZZZ" + + const val MAX_MMS_ATTACHMENT_SIZE: Long = (3L * 1024 * 1024) / 2 } } diff --git a/android/app/src/main/java/com/httpsms/Encrypter.kt b/android/app/src/main/java/com/httpsms/Encrypter.kt new file mode 100644 index 00000000..970ea067 --- /dev/null +++ b/android/app/src/main/java/com/httpsms/Encrypter.kt @@ -0,0 +1,44 @@ +package com.httpsms + +import timber.log.Timber +import java.security.MessageDigest +import java.util.Base64 +import java.util.Random +import javax.crypto.Cipher +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec + +object Encrypter { + private const val ALGORITHM = "AES/CFB/NoPadding" + private const val IV_SIZE = 16 + + fun decrypt(key: String, cipherText: String): String { + val cipher = Cipher.getInstance(ALGORITHM) + val cipherBytes = Base64.getDecoder().decode(cipherText) + Timber.d("iv = ${Base64.getEncoder().encodeToString(cipherBytes.take(IV_SIZE).toByteArray())}") + Timber.d("cipher = ${Base64.getEncoder().encodeToString(cipherBytes.drop(IV_SIZE).toByteArray())}") + cipher.init(Cipher.DECRYPT_MODE, SecretKeySpec(hash(key), "AES"), IvParameterSpec(cipherBytes.take(IV_SIZE).toByteArray())) + val plainText = cipher.doFinal(cipherBytes.drop(IV_SIZE).toByteArray()) + return String(plainText) + } + + fun encrypt(key: String, inputText: String): String { + val cipher = Cipher.getInstance(ALGORITHM) + val iv = generateIv() + cipher.init(Cipher.ENCRYPT_MODE, SecretKeySpec(hash(key),"AES"), IvParameterSpec(iv)) + val cipherBytes = iv + cipher.doFinal(inputText.toByteArray()) + return Base64.getEncoder().encodeToString(cipherBytes) + } + + private fun generateIv(): ByteArray { + val b = ByteArray(IV_SIZE) + Random().nextBytes(b) + return b + } + + private fun hash(key: String): ByteArray { + val bytes = key.toByteArray() + val md = MessageDigest.getInstance("SHA-256") + return md.digest(bytes) + } +} diff --git a/android/app/src/main/java/com/httpsms/FirebaseMessagingService.kt b/android/app/src/main/java/com/httpsms/FirebaseMessagingService.kt index b63b759a..e4113289 100644 --- a/android/app/src/main/java/com/httpsms/FirebaseMessagingService.kt +++ b/android/app/src/main/java/com/httpsms/FirebaseMessagingService.kt @@ -6,9 +6,17 @@ import android.content.Intent import androidx.work.* import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.RemoteMessage +import com.httpsms.SentReceiver.FailedMessageWorker import timber.log.Timber -import java.time.ZoneOffset -import java.time.ZonedDateTime + +import com.google.android.mms.pdu_alt.CharacterSets +import com.google.android.mms.pdu_alt.EncodedStringValue +import com.google.android.mms.pdu_alt.PduBody +import com.google.android.mms.pdu_alt.PduComposer +import com.google.android.mms.pdu_alt.PduPart +import com.google.android.mms.pdu_alt.SendReq +import okhttp3.MediaType +import java.io.File class MyFirebaseMessagingService : FirebaseMessagingService() { // [START receive_message] @@ -57,7 +65,13 @@ class MyFirebaseMessagingService : FirebaseMessagingService() { } Thread { try { - HttpSmsApiService.create(applicationContext).storeHeartbeat(Settings.getSIM1PhoneNumber(applicationContext), Settings.isCharging(applicationContext)) + val phoneNumbers = mutableListOf() + phoneNumbers.add(Settings.getSIM1PhoneNumber(applicationContext)) + if (Settings.getActiveStatus(applicationContext, Constants.SIM2)) { + phoneNumbers.add(Settings.getSIM2PhoneNumber(applicationContext)) + } + + HttpSmsApiService.create(applicationContext).storeHeartbeat(phoneNumbers.toTypedArray(), Settings.isCharging(applicationContext)) Settings.setHeartbeatTimestampAsync(applicationContext, System.currentTimeMillis()) } catch (exception: Exception) { Timber.e(exception) @@ -93,15 +107,15 @@ class MyFirebaseMessagingService : FirebaseMessagingService() { if (Settings.isLoggedIn(this)) { Timber.d("updating SIM1 phone with new fcm token") - val phone = HttpSmsApiService.create(this).updatePhone(Settings.getSIM1PhoneNumber(this), token, Constants.SIM1) - if (phone != null) { - Settings.setUserID(this, phone.userID) + val response = HttpSmsApiService.create(this).updateFcmToken(Settings.getSIM1PhoneNumber(this), Constants.SIM1, token) + if (response.first != null) { + Settings.setUserID(this, response.first!!.userID) } } if(Settings.isDualSIM(this)) { Timber.d("updating SIM2 phone with new fcm token") - HttpSmsApiService.create(this).updatePhone(Settings.getSIM2PhoneNumber(this), token, Constants.SIM2) + HttpSmsApiService.create(this).updateFcmToken(Settings.getSIM2PhoneNumber(this), Constants.SIM2, token) } } @@ -111,7 +125,7 @@ class MyFirebaseMessagingService : FirebaseMessagingService() { return } - if (BuildConfig.DEBUG) { + if(Settings.isDebugLogEnabled(this)) { Timber.plant(Timber.DebugTree()) Timber.plant(LogzTree(this.applicationContext)) } @@ -133,18 +147,175 @@ class MyFirebaseMessagingService : FirebaseMessagingService() { val message = getMessage(applicationContext, messageID) ?: return Result.failure() if (!Settings.getActiveStatus(applicationContext, message.sim)) { Timber.w("[${message.sim}] SIM is not active, stopping processing") - handleFailed(applicationContext, messageID) + handleFailed(applicationContext, messageID, "Outgoing messages have been disabled on the mobile app") + return Result.failure() + } + + if (message.encrypted && Settings.getEncryptionKey(applicationContext).isNullOrEmpty()) { + Timber.w("[${message.sim}] message is encrypted but the encryption key is empty") + handleFailed(applicationContext, messageID, "Outgoing message is encrypted but mobile app has no encryption key") return Result.failure() } + if (message.encrypted) { + try { + Encrypter.decrypt(Settings.getEncryptionKey(applicationContext)!!, message.content) + } catch (exception: Exception) { + Timber.e(exception) + handleFailed(applicationContext, messageID, "Cannot decrypt the outgoing message. Check your encryption key on the Android app.") + return Result.failure() + } + } Receiver.register(applicationContext) + + if (message.attachments != null && message.attachments.isNotEmpty()) { + return handleMmsMessage(message) + } + val parts = getMessageParts(applicationContext, message) if (parts.size == 1) { - return handleSingleMessage(message) + return handleSingleMessage(message, parts.first()) } return handleMultipartMessage(message, parts) } + fun extractFileName(url: String, prefix: String, mimeType: String? = null): String { + val fileName = url.substringAfterLast("/") + .substringBefore("?") + .takeIf { it.isNotBlank() && it.contains(".") } + ?: run { + val extension = mimeType?.let { mime -> + val ext = mime.substringAfterLast("/") + if (ext.isNotBlank()) ".$ext" else ".bin" + } ?: "" + "attachment$extension" + } + + return "${prefix}_$fileName" + } + + private fun handleMmsMessage(message: Message): Result { + Timber.d("Processing MMS for message ID [${message.id}]") + val apiService = HttpSmsApiService.create(applicationContext) + + val downloadedFiles = mutableListOf>() + + try { + for ((index, attachment) in message.attachments!!.withIndex()) { + val file = apiService.downloadAttachment(applicationContext, attachment, message.id, index) + if (file.first == null || file.second == null) { + handleFailed(applicationContext, message.id, "Failed to download attachment or file size exceeded 1.5MB.") + return Result.failure() + } + downloadedFiles.add(Pair(file.first!!, file.second!!)) + } + + val sendReq = SendReq() + + val encodedContact = EncodedStringValue(message.contact) + sendReq.to = arrayOf(encodedContact) + + val pduBody = PduBody() + + if (message.content.isNotEmpty()) { + val textPart = PduPart() + textPart.setCharset(CharacterSets.UTF_8) + textPart.contentType = "text/plain".toByteArray() + textPart.name = "text".toByteArray() + textPart.contentId = "text".toByteArray() + textPart.contentLocation = "text".toByteArray() + + var messageBody = message.content + val encryptionKey = Settings.getEncryptionKey(applicationContext) + if (message.encrypted && !encryptionKey.isNullOrEmpty()) { + messageBody = Encrypter.decrypt(encryptionKey, messageBody) + } + textPart.data = messageBody.toByteArray(Charsets.UTF_8) + + pduBody.addPart(textPart) + } + + for ((index, file) in downloadedFiles.withIndex()) { + val fileBytes = file.first.readBytes() + + val mediaPart = PduPart() + mediaPart.contentType = file.second.toString().toByteArray() + + + val fileName = extractFileName(message.attachments[index], index.toString(), file.second.toString()) + mediaPart.name = fileName.toByteArray() + mediaPart.contentId = fileName.toByteArray() + mediaPart.contentLocation = fileName.toByteArray() + mediaPart.data = fileBytes + + Timber.d("Adding MMS attachment with name [$fileName] and size [${fileBytes.size}] and type [${file.second}]") + + pduBody.addPart(mediaPart) + } + + sendReq.body = pduBody + + val pduComposer = PduComposer(applicationContext, sendReq) + val pduBytes = pduComposer.make() + + if (pduBytes == null) { + Timber.e("PduComposer failed to generate PDU byte array") + handleFailed(applicationContext, message.id, "Failed to compose MMS PDU.") + return Result.failure() + } + + val mmsDir = java.io.File(applicationContext.cacheDir, "mms_attachments") + if (!mmsDir.exists()) { + mmsDir.mkdirs() + } + + val pduFile = java.io.File(mmsDir, "pdu_${message.id}.dat") + java.io.FileOutputStream(pduFile).use { it.write(pduBytes) } + + val pduUri = androidx.core.content.FileProvider.getUriForFile( + applicationContext, + "${BuildConfig.APPLICATION_ID}.fileprovider", + pduFile + ) + + val sentIntent = createPendingIntent(message.id, SmsManagerService.sentAction()) + SmsManagerService().sendMultimediaMessage(applicationContext, pduUri, message.sim, sentIntent) + + Timber.d("Successfully dispatched MMS for message ID [${message.id}]") + return Result.success() + + } catch (e: Exception) { + Timber.e(e, "Failed to send MMS for message ID [${message.id}]") + handleFailed(applicationContext, message.id, e.message ?: "Internal error while building or sending MMS.") + return Result.failure() + } finally { + // Clean up any downloaded temporary files + downloadedFiles.forEach { file -> + if (file.first.exists()) { + file.first.delete() + } + } + + // Also clean up the MMS PDU file to avoid cache buildup in cases where + // sendMultimediaMessage fails before the sent broadcast is delivered. + try { + // The PDU file is stored under the "mms_attachments" cache subdirectory; + // delete it from the same location to ensure cleanup is effective. + val pduDir = File(applicationContext.cacheDir, "mms_attachments") + val pduFile = File(pduDir, "pdu_${message.id}.dat") + if (pduFile.exists()) { + val deleted = pduFile.delete() + if (!deleted) { + Timber.w("Failed to delete MMS PDU file for message ID [${message.id}] at [${pduFile.absolutePath}]") + } + } + } catch (cleanupException: Exception) { + // Best-effort cleanup; log but do not change the original result. + Timber.w(cleanupException, "Error while cleaning up MMS PDU file for message ID [${message.id}]") + } + } + } + private fun handleMultipartMessage(message:Message, parts: ArrayList): Result { Timber.d("sending multipart SMS for message with ID [${message.id}]") return try { @@ -169,21 +340,22 @@ class MyFirebaseMessagingService : FirebaseMessagingService() { } catch (e: Exception) { Timber.e(e) Timber.d("could not send SMS for message with ID [${message.id}] in [${parts.size}] parts") + handleFailed(this.applicationContext, message.id, e.message ?: e.javaClass.simpleName) Result.failure() } } - - private fun handleSingleMessage(message:Message): Result { + private fun handleSingleMessage(message:Message, content: String): Result { sendMessage( message, + content, createPendingIntent(message.id, SmsManagerService.sentAction()), createPendingIntent(message.id, SmsManagerService.deliveredAction()) ) return Result.success() } - private fun handleFailed(context: Context, messageID: String) { + private fun handleFailed(context: Context, messageID: String, reason: String) { Timber.d("sending [FAILED] event for message with ID [${messageID}]") val constraints = Constraints.Builder() @@ -192,12 +364,12 @@ class MyFirebaseMessagingService : FirebaseMessagingService() { val inputData: Data = workDataOf( Constants.KEY_MESSAGE_ID to messageID, - Constants.KEY_MESSAGE_REASON to "MOBILE_APP_INACTIVE", + Constants.KEY_MESSAGE_REASON to reason, Constants.KEY_MESSAGE_TIMESTAMP to Settings.currentTimestamp() ) val work = OneTimeWorkRequest - .Builder(SentReceiver.FailedMessageWorker::class.java) + .Builder(FailedMessageWorker::class.java) .setConstraints(constraints) .setInputData(inputData) .build() @@ -222,13 +394,14 @@ class MyFirebaseMessagingService : FirebaseMessagingService() { return null } - private fun sendMessage(message: Message, sentIntent: PendingIntent, deliveredIntent: PendingIntent) { + private fun sendMessage(message: Message, content: String, sentIntent: PendingIntent, deliveredIntent: PendingIntent) { Timber.d("sending SMS for message with ID [${message.id}]") try { - SmsManagerService().sendTextMessage(this.applicationContext,message.contact, message.content, message.sim, sentIntent, deliveredIntent) + SmsManagerService().sendTextMessage(this.applicationContext,message.contact, content, message.sim, sentIntent, deliveredIntent) } catch (e: Exception) { Timber.e(e) Timber.d("could not send SMS for message with ID [${message.id}]") + handleFailed(this.applicationContext, message.id, e.message ?: e.javaClass.simpleName) return } Timber.d("sent SMS for message with ID [${message.id}]") @@ -236,15 +409,22 @@ class MyFirebaseMessagingService : FirebaseMessagingService() { private fun getMessageParts(context: Context, message: Message): ArrayList { Timber.d("getting parts for message with ID [${message.id}]") + + var messageBody = message.content + val encryptionKey = Settings.getEncryptionKey(context) + if (message.encrypted && !encryptionKey.isNullOrEmpty()) { + messageBody = Encrypter.decrypt(encryptionKey, messageBody) + } + return try { - val parts = SmsManagerService().messageParts(context, message.content) + val parts = SmsManagerService().messageParts(context, messageBody) Timber.d("message with ID [${message.id}] has [${parts.size}] parts") parts } catch (e: Exception) { Timber.e(e) Timber.d("could not get parts message with ID [${message.id}] returning [1] part with entire content") val list = ArrayList() - list.add(message.content) + list.add(messageBody) list } } diff --git a/android/app/src/main/java/com/httpsms/HttpSmsApiService.kt b/android/app/src/main/java/com/httpsms/HttpSmsApiService.kt index 4bc754dc..51fa21cd 100644 --- a/android/app/src/main/java/com/httpsms/HttpSmsApiService.kt +++ b/android/app/src/main/java/com/httpsms/HttpSmsApiService.kt @@ -1,17 +1,20 @@ package com.httpsms import android.content.Context -import android.os.BatteryManager +import com.httpsms.Constants.Companion.MAX_MMS_ATTACHMENT_SIZE +import okhttp3.MediaType import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.RequestBody.Companion.toRequestBody -import org.apache.commons.text.StringEscapeUtils import timber.log.Timber +import java.io.File +import java.io.FileOutputStream +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream import java.net.URI import java.net.URL -import java.time.ZonedDateTime -import java.time.format.DateTimeFormatter import java.util.logging.Level import java.util.logging.Logger.getLogger @@ -71,10 +74,37 @@ class HttpSmsApiService(private val apiKey: String, private val baseURL: URI) { return sendEvent(messageId, "FAILED", timestamp, reason) } - fun receive(sim: String, from: String, to: String, content: String, timestamp: String): Boolean { + fun receive(requestPayload: ReceivedMessageRequest): Boolean { + val body = com.beust.klaxon.Klaxon().toJsonString(requestPayload) + + val request: Request = Request.Builder() + .url(resolveURL("/v1/messages/receive")) + .post(body.toRequestBody(jsonMediaType)) + .header(apiKeyHeader, apiKey) + .header(clientVersionHeader, BuildConfig.VERSION_NAME) + .build() + + val response = try { + client.newCall(request).execute() + } catch (e: Exception) { + Timber.e(e, "Exception while sending received message request") + return false + } + + if (!response.isSuccessful) { + Timber.e("error response [${response.body?.string()}] with code [${response.code}] while receiving message") + response.close() + return response.code in 400..499 + } + + response.close() + Timber.i("received message stored successfully") + return true + } + + fun sendMissedCallEvent(sim: String, from: String, to: String, timestamp: String): Boolean { val body = """ { - "content": "${StringEscapeUtils.escapeJson(content)}", "sim": "$sim", "from": "$from", "timestamp": "$timestamp", @@ -83,7 +113,7 @@ class HttpSmsApiService(private val apiKey: String, private val baseURL: URI) { """.trimIndent() val request: Request = Request.Builder() - .url(resolveURL("/v1/messages/receive")) + .url(resolveURL("/v1/messages/calls/missed")) .post(body.toRequestBody(jsonMediaType)) .header(apiKeyHeader, apiKey) .header(clientVersionHeader, BuildConfig.VERSION_NAME) @@ -91,22 +121,21 @@ class HttpSmsApiService(private val apiKey: String, private val baseURL: URI) { val response = client.newCall(request).execute() if (!response.isSuccessful) { - Timber.e("error response [${response.body?.string()}] with code [${response.code}] while receiving message [${body}]") + Timber.e("error response [${response.body?.string()}] with code [${response.code}] while sending missed call event [${body}]") response.close() - return false + return response.code in 400..499 } - val message = ResponseMessage.fromJson(response.body!!.string()) response.close() - Timber.i("received message stored successfully for message with ID [${message?.data?.id}]" ) - return true; + Timber.i("missed call from [${from}] to [${to}] sent successfully with timestamp [${timestamp}]" ) + return true } - fun storeHeartbeat(phoneNumber: String, charging: Boolean) { + fun storeHeartbeat(phoneNumbers: Array, charging: Boolean): Boolean { val body = """ { "charging": $charging, - "owner": "$phoneNumber" + "phone_numbers": ${phoneNumbers.joinToString(prefix = "[", postfix = "]") { "\"$it\"" }} } """.trimIndent() @@ -119,15 +148,75 @@ class HttpSmsApiService(private val apiKey: String, private val baseURL: URI) { val response = client.newCall(request).execute() if (!response.isSuccessful) { - Timber.e("error response [${response.body?.string()}] with code [${response.code}] while sending heartbeat [$body] for owner [$phoneNumber]") + Timber.e("error response [${response.body?.string()}] with code [${response.code}] while sending heartbeat [$body] for phone numbers [${phoneNumbers.joinToString()}]") response.close() - return + return false } response.close() - Timber.i( "heartbeat stored successfully for owner [$phoneNumber]" ) + Timber.i( "heartbeat stored successfully for phone numbers [${phoneNumbers.joinToString()}]" ) + return true + } + + fun InputStream.copyToWithLimit( + out: OutputStream, + limit: Long, + bufferSize: Int = DEFAULT_BUFFER_SIZE + ): Long { + var bytesCopied: Long = 0 + val buffer = ByteArray(bufferSize) + var bytes = read(buffer) + + while (bytes >= 0) { + bytesCopied += bytes + + if (bytesCopied > limit) { + throw IOException("Download aborted: File exceeded maximum allowed size of $limit bytes.") + } + + out.write(buffer, 0, bytes) + bytes = read(buffer) + } + return bytesCopied } + fun downloadAttachment(context: Context, urlString: String, messageId: String, attachmentIndex: Int): Pair { + val request = Request.Builder().url(urlString).build() + + try { + client.newCall(request).execute().use { response -> + if (!response.isSuccessful) { + Timber.e("Failed to download attachment: ${response.code}") + return Pair(null, null) + } + + val body = response.body + val contentLength = body.contentLength() + if (contentLength > MAX_MMS_ATTACHMENT_SIZE) { + Timber.e("Attachment is too large ($contentLength bytes).") + return Pair(null, null) + } + + val mmsDir = File(context.cacheDir, "mms_attachments") + if (!mmsDir.exists()) { + mmsDir.mkdirs() + } + + val tempFile = File(mmsDir, "mms_${messageId}_$attachmentIndex") + val inputStream = body.byteStream() + FileOutputStream(tempFile).use { outputStream -> + inputStream.use { input -> + input.copyToWithLimit(outputStream, MAX_MMS_ATTACHMENT_SIZE) + } + } + + return Pair(tempFile, body.contentType()) + } + } catch (e: Exception) { + Timber.e(e, "Exception while download attachment") + return Pair(null, null) + } + } private fun sendEvent(messageId: String, event: String, timestamp: String, reason: String? = null): Boolean { var reasonString = "null" @@ -151,8 +240,14 @@ class HttpSmsApiService(private val apiKey: String, private val baseURL: URI) { .build() val response = client.newCall(request).execute() + if (response.code == 404) { + response.close() + Timber.i( "[$event] event sent successfully but message with ID [$messageId] has been deleted" ) + return true + } + if (!response.isSuccessful) { - Timber.e("error response [${response.body?.string()}] with code [${response.code}] while sending [${event}] event [${body}] for message with ID [${messageId}]") + Timber.e("error response [${response.body.string()}] with code [${response.code}] while sending [${event}] event [${body}] for message with ID [${messageId}]") response.close() return false } @@ -162,8 +257,7 @@ class HttpSmsApiService(private val apiKey: String, private val baseURL: URI) { return true } - - fun updatePhone(phoneNumber: String, fcmToken: String, sim: String): Phone? { + fun updateFcmToken(phoneNumber: String, sim: String, fcmToken: String): Triple { val body = """ { "fcm_token": "$fcmToken", @@ -173,47 +267,30 @@ class HttpSmsApiService(private val apiKey: String, private val baseURL: URI) { """.trimIndent() val request: Request = Request.Builder() - .url(resolveURL("/v1/phones")) + .url(resolveURL("/v1/phones/fcm-token")) .put(body.toRequestBody(jsonMediaType)) .header(apiKeyHeader, apiKey) .header(clientVersionHeader, BuildConfig.VERSION_NAME) .build() - val response = client.newCall(request).execute() - if (!response.isSuccessful) { - Timber.e("error response [${response.body?.string()}] with code [${response.code}] while sending fcm token [${body}]") - response.close() - return null - } - - val payload = ResponsePhone.fromJson(response.body!!.string())?.data - response.close() - Timber.i("fcm token sent successfully for phone [$phoneNumber]" ) - return payload - } - - - fun validateApiKey(): Pair { - val request: Request = Request.Builder() - .url(resolveURL("/v1/users/me")) - .header(apiKeyHeader, apiKey) - .header(clientVersionHeader, BuildConfig.VERSION_NAME) - .get() - .build() - try { val response = client.newCall(request).execute() if (!response.isSuccessful) { - Timber.e("error response [${response.body?.string()}] with code [${response.code}] while verifying apiKey [$apiKey]") + Timber.e("error response [${response.body?.string()}] with code [${response.code}] while updating FCM token [$fcmToken] with apiKey [$apiKey]") response.close() - return Pair("Cannot validate the API key. Check if it is correct and try again.", null) + if (response.code == 401) { + Timber.e("invalid API key [$apiKey]") + return Triple(null, "Cannot validate the API key. Check if it is correct and try again.", null) + } + return Triple(null,null, "Cannot login to the server, Make sure the phone number is in international format e.g +18005550100") } + Timber.i("FCM token submitted correctly with API key [$apiKey] and server url [$baseURL]" ) + val payload = ResponsePhone.fromJson(response.body!!.string())?.data response.close() - Timber.i("api key [$apiKey] and server url [$baseURL] are valid" ) - return Pair(null, null) + return Triple(payload, null, null) } catch (ex: Exception) { - return Pair(null, ex.message) + return Triple(null, null, ex.message) } } diff --git a/android/app/src/main/java/com/httpsms/LoginActivity.kt b/android/app/src/main/java/com/httpsms/LoginActivity.kt index fc089039..babe12df 100644 --- a/android/app/src/main/java/com/httpsms/LoginActivity.kt +++ b/android/app/src/main/java/com/httpsms/LoginActivity.kt @@ -7,22 +7,28 @@ import android.content.Intent import android.content.pm.PackageManager import android.os.Build import android.os.Bundle -import android.telephony.PhoneNumberUtils import android.telephony.TelephonyManager import android.view.View import android.webkit.URLUtil import android.widget.LinearLayout +import android.widget.Toast import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.core.app.ActivityCompat import androidx.lifecycle.MutableLiveData +import com.google.android.gms.common.ConnectionResult +import com.google.android.gms.common.GoogleApiAvailability import com.google.android.material.button.MaterialButton import com.google.android.material.progressindicator.LinearProgressIndicator import com.google.android.material.textfield.TextInputEditText import com.google.android.material.textfield.TextInputLayout +import com.httpsms.validators.PhoneNumberValidator +import com.journeyapps.barcodescanner.ScanContract +import com.journeyapps.barcodescanner.ScanOptions import timber.log.Timber import java.net.URI + class LoginActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -32,6 +38,39 @@ class LoginActivity : AppCompatActivity() { setPhoneNumber() disableSim2() setServerURL() + setupApiKeyInput() + } + + private fun setupApiKeyInput() { + val apiKeyInputLayout = findViewById(R.id.loginApiKeyTextInputLayout) + val apiKeyInput = findViewById(R.id.loginApiKeyTextInput) + + apiKeyInput.setOnClickListener { + startQrCodeScan() + } + + apiKeyInputLayout.setEndIconOnClickListener { + startQrCodeScan() + } + } + + private val barcodeLauncher = registerForActivityResult(ScanContract()) { result -> + if (result.contents != null) { + val apiKeyInput = findViewById(R.id.loginApiKeyTextInput) + apiKeyInput.setText(result.contents) + Toast.makeText(this, "Scanned: ${result.contents}", Toast.LENGTH_LONG).show() + } else { + Toast.makeText(this, "Scan cancelled", Toast.LENGTH_SHORT).show() + } + } + + private fun startQrCodeScan() { + val options = ScanOptions() + options.setPrompt("Scan a QR code") + options.setBeepEnabled(true) + options.setOrientationLocked(false) + options.setCameraId(0) + barcodeLauncher.launch(options) } override fun onStart() { @@ -81,6 +120,7 @@ class LoginActivity : AppCompatActivity() { } @SuppressLint("HardwareIds") + @Suppress("DEPRECATION") private fun getPhoneNumber(context: Context): String? { val telephonyManager = this.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager if (ActivityCompat.checkSelfPermission( @@ -123,9 +163,35 @@ class LoginActivity : AppCompatActivity() { Timber.d("creating permissions launcher") } + private fun isGooglePlayServicesAvailable(): String? { + val googleApiAvailability = GoogleApiAvailability.getInstance() + val status = googleApiAvailability.isGooglePlayServicesAvailable(this) + if (status != ConnectionResult.SUCCESS) { + if (googleApiAvailability.isUserResolvableError(status)) { + googleApiAvailability.getErrorDialog(this, status, 2404)?.show() + } + return googleApiAvailability.getErrorString(status) + } + return null + } + private fun onLoginClick() { Timber.d("login button clicked") + + val error = isGooglePlayServicesAvailable() + if (error != null) { + Timber.d("google play services not installed [${error}]") + Toast.makeText(this, error, Toast.LENGTH_SHORT).show() + return + } + + if (Settings.getFcmToken(this) == null) { + Timber.d("The FCM token is not set") + Toast.makeText(this, "Cannot find FCM token. Make sure you have Google Play Services installed", Toast.LENGTH_LONG).show() + return + } + loginButton().isEnabled = false val progressBar = findViewById(R.id.loginProgressIndicator) progressBar.visibility = View.VISIBLE @@ -154,6 +220,8 @@ class LoginActivity : AppCompatActivity() { val phoneNumberSIM2 = findViewById(R.id.loginPhoneNumberInputSIM2) phoneNumberSIM2.isEnabled = false + val countryCode = getCountryCode() + val resetView = fun () { apiKey.isEnabled = true serverUrl.isEnabled = true @@ -163,25 +231,17 @@ class LoginActivity : AppCompatActivity() { loginButton().isEnabled = true } - if ( - !PhoneNumberUtils.isWellFormedSmsAddress(phoneNumber.text.toString().trim()) || - !PhoneNumberUtils.isGlobalPhoneNumber(phoneNumber.text.toString().trim()) - ) { + if (!PhoneNumberValidator.isValidPhoneNumber(phoneNumber.text.toString().trim(), countryCode)) { Timber.e("[SIM1] phone number [${phoneNumber.text.toString()}] is not valid") resetView() - phoneNumberLayout.error = "Invalid E.164 phone number" + phoneNumberLayout.error = "Enter an international phone number in the E.164 format" return } - if ( - SmsManagerService.isDualSIM(this) && ( - !PhoneNumberUtils.isWellFormedSmsAddress(phoneNumberSIM2.text.toString().trim()) || - !PhoneNumberUtils.isGlobalPhoneNumber(phoneNumberSIM2.text.toString().trim()) - ) - ) { + if (SmsManagerService.isDualSIM(this) && !PhoneNumberValidator.isValidPhoneNumber(phoneNumberSIM2.text.toString().trim(), countryCode)) { Timber.e("[SIM2] phone number [${phoneNumberSIM2.text.toString()}] is not valid") resetView() - phoneNumberLayoutSIM2.error = "Invalid E.164 phone number" + phoneNumberLayoutSIM2.error = "Enter an international phone number in the E.164 format" return } @@ -218,11 +278,11 @@ class LoginActivity : AppCompatActivity() { Settings.setApiKeyAsync(this, apiKey.text.toString()) Settings.setServerUrlAsync(this, serverUrl.text.toString().trim()) - val e164PhoneNumber = formatE164(phoneNumber.text.toString().trim()) + val e164PhoneNumber = PhoneNumberValidator.formatE164(phoneNumber.text.toString().trim(), countryCode) Settings.setSIM1PhoneNumber(this, e164PhoneNumber) if(SmsManagerService.isDualSIM(this)) { - val sim2PhoneNumber = formatE164(phoneNumberSIM2.text.toString().trim()) + val sim2PhoneNumber = PhoneNumberValidator.formatE164(phoneNumberSIM2.text.toString().trim(), countryCode) Settings.setSIM2PhoneNumber(this, sim2PhoneNumber) } @@ -232,37 +292,28 @@ class LoginActivity : AppCompatActivity() { } Thread { - val error = HttpSmsApiService(apiKey.text.toString(), URI(serverUrl.text.toString().trim())).validateApiKey() - liveData.postValue(error) - Timber.d("finished validating api URL") - }.start() - } - - private fun formatE164(number: String): String { - var phoneNumber = number.trim() - if (!number.startsWith("+")) { - phoneNumber = "+$number" - } - - Timber.d("formatting phone number [${phoneNumber}] into e164") - - val formattedNumber = PhoneNumberUtils.formatNumberToE164( - phoneNumber, - this.resources.configuration.locales.get(0).country - ) + val service = HttpSmsApiService(apiKey.text.toString(), URI(serverUrl.text.toString().trim())) + + var e164PhoneNumber = PhoneNumberValidator.formatE164(phoneNumber.text.toString().trim(), countryCode) + var response = service.updateFcmToken(e164PhoneNumber, Constants.SIM1, Settings.getFcmToken(this) ?: "") + if(response.second != null || response.third != null) { + Timber.e("error updating fcm token [${response.second}], third [${response.third}]") + liveData.postValue(Pair(response.second, response.third)) + return@Thread + } - if (formattedNumber !== null) { - return formattedNumber - } + if (!SmsManagerService.isDualSIM(this)) { + Timber.d("single sim detected, no need to update sim2") + liveData.postValue(Pair(null, null)) + return@Thread + } - return phoneNumber; - } + e164PhoneNumber = PhoneNumberValidator.formatE164(phoneNumberSIM2.text.toString().trim(), countryCode) + response = service.updateFcmToken(e164PhoneNumber, Constants.SIM2, Settings.getFcmToken(this) ?: "") - private fun addPlus(number: String): String { - if (number.startsWith("+")) { - return number - } - return "+$number" + liveData.postValue(Pair(response.second, response.third)) + Timber.d("finished validating api URL") + }.start() } private fun redirectToMain() { @@ -278,4 +329,18 @@ class LoginActivity : AppCompatActivity() { private fun loginButton(): MaterialButton { return findViewById(R.id.loginButton) } + + private fun getCountryCode() : String { + // Get the TelephonyManager from the system services + val tm = this.getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager + + // Get the network country ISO code and convert it to uppercase + val code = tm.networkCountryIso.uppercase() + + // If the country code is empty, retrieve the country code from the device's locale + if (code.isEmpty()) { + return this.resources.configuration.locales.get(0).country.uppercase() + } + return code + } } diff --git a/android/app/src/main/java/com/httpsms/LogzTree.kt b/android/app/src/main/java/com/httpsms/LogzTree.kt index 8c051ed7..68a81c8e 100644 --- a/android/app/src/main/java/com/httpsms/LogzTree.kt +++ b/android/app/src/main/java/com/httpsms/LogzTree.kt @@ -35,11 +35,12 @@ class LogzTree(val context: Context): Timber.DebugTree() { t ) - val body = Klaxon().toJsonString(logEntry).toRequestBody("application/x-www-form-urlencoded".toMediaType()) + val body = Klaxon().toJsonString(listOf(logEntry)).toRequestBody("application/json".toMediaType()) val request: Request = Request.Builder() - .url("https://listener.logz.io:8071?token=cTCUVJoTDrPjaFcanAPRzIsYyThyrIDw&type=http-bulk") + .url("https://api.axiom.co/v1/datasets/production/ingest") .post(body) - .header("Content-Type", "application/x-www-form-urlencoded") + .header("Content-Type", "application/json") + .header("Authorization", "Bearer xaat-2a2e0b73-3702-4971-a80f-be3956934950") .build() Thread { diff --git a/android/app/src/main/java/com/httpsms/MainActivity.kt b/android/app/src/main/java/com/httpsms/MainActivity.kt index 23b59144..363e7c19 100644 --- a/android/app/src/main/java/com/httpsms/MainActivity.kt +++ b/android/app/src/main/java/com/httpsms/MainActivity.kt @@ -6,7 +6,7 @@ import android.app.NotificationChannel import android.app.NotificationManager import android.content.Context import android.content.Intent -import android.content.IntentFilter +import android.content.pm.PackageManager import android.net.Uri import android.os.Build import android.os.Bundle @@ -29,7 +29,6 @@ import com.google.android.material.card.MaterialCardView import com.google.android.material.progressindicator.LinearProgressIndicator import com.httpsms.services.StickyNotificationService import com.httpsms.worker.HeartbeatWorker -import okhttp3.internal.format import timber.log.Timber import java.time.Instant import java.time.ZoneId @@ -42,9 +41,6 @@ import android.provider.Settings as ProviderSettings class MainActivity : AppCompatActivity() { - private var sentReceiver: SentReceiver? = null - private var deliveredReceiver: DeliveredReceiver? = null - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -64,6 +60,7 @@ class MainActivity : AppCompatActivity() { scheduleHeartbeatWorker(this) setVersion() setHeartbeatListener(this) + setSmsPermissionListener() setBatteryOptimizationListener() } @@ -78,12 +75,13 @@ class MainActivity : AppCompatActivity() { redirectToLogin() refreshToken(this) setCardContent(this) + setSmsPermissionListener() setBatteryOptimizationListener() } private fun setVersion() { val appVersionView = findViewById(R.id.mainAppVersion) - appVersionView.text = format(getString(R.string.app_version), BuildConfig.VERSION_NAME) + appVersionView.text = getString(R.string.app_version, BuildConfig.VERSION_NAME) } private fun setCardContent(context: Context) { @@ -108,15 +106,17 @@ class MainActivity : AppCompatActivity() { } private fun requestPermissions(context:Context) { - if(!Settings.isLoggedIn(context)) { - return - } - Timber.d("requesting permissions") val requestPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> permissions.entries.forEach { Timber.d("${it.key} = ${it.value}") + if (it.key == Manifest.permission.READ_CALL_LOG && !it.value) { + Timber.w("disabling incoming call events since for SIM1 and SIM2") + Settings.setIncomingCallEventsEnabled(context, Constants.SIM1, false) + Settings.setIncomingCallEventsEnabled(context, Constants.SIM2, false) + } } + setSmsPermissionListener() } var permissions = arrayOf( @@ -129,6 +129,11 @@ class MainActivity : AppCompatActivity() { permissions += Manifest.permission.POST_NOTIFICATIONS } + if(Settings.isIncomingCallEventsEnabled(context,Constants.SIM1) || Settings.isIncomingCallEventsEnabled(context,Constants.SIM2) ) { + permissions += Manifest.permission.READ_CALL_LOG + permissions += Manifest.permission.READ_PHONE_STATE + } + requestPermissionLauncher.launch(permissions) Timber.d("creating permissions launcher") @@ -200,9 +205,9 @@ class MainActivity : AppCompatActivity() { private fun sendFCMToken(timestamp: Long, context:Context, phoneNumber: String, sim: String) { Thread { - val phone = HttpSmsApiService.create(context).updatePhone(phoneNumber, Settings.getFcmToken(context) ?: "", sim) - if (phone != null) { - Settings.setUserID(context, phone.userID) + val response = HttpSmsApiService.create(context).updateFcmToken(phoneNumber, sim,Settings.getFcmToken(context) ?: "") + if (response.first != null) { + Settings.setUserID(context, response.first!!.userID) Settings.setFcmTokenLastUpdateTimestampAsync(context, timestamp) Timber.i("[${sim}] FCM token uploaded successfully") return@Thread @@ -218,7 +223,7 @@ class MainActivity : AppCompatActivity() { return } - if (BuildConfig.DEBUG) { + if(Settings.isDebugLogEnabled(this)) { Timber.plant(Timber.DebugTree()) Timber.plant(LogzTree(this.applicationContext)) } @@ -281,8 +286,9 @@ class MainActivity : AppCompatActivity() { @SuppressLint("BatteryLife") private fun setBatteryOptimizationListener() { val pm = getSystemService(POWER_SERVICE) as PowerManager + val button = findViewById(R.id.batteryOptimizationButtonButton) if (!pm.isIgnoringBatteryOptimizations(packageName)) { - val button = findViewById(R.id.batteryOptimizationButtonButton) + button.visibility = View.VISIBLE button.setOnClickListener { val intent = Intent() intent.action = ProviderSettings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS @@ -290,8 +296,43 @@ class MainActivity : AppCompatActivity() { startActivity(intent) } } else { - val layout = findViewById(R.id.batteryOptimizationLinearLayout) + button.visibility = View.GONE + } + updatePermissionLayoutVisibility() + } + + private fun setSmsPermissionListener() { + val smsPermissions = arrayOf( + Manifest.permission.SEND_SMS, + Manifest.permission.RECEIVE_SMS, + Manifest.permission.READ_SMS + ) + val allGranted = smsPermissions.all { + checkSelfPermission(it) == PackageManager.PERMISSION_GRANTED + } + + val button = findViewById(R.id.smsPermissionButton) + if (!allGranted) { + button.visibility = View.VISIBLE + button.setOnClickListener { + val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://httpsms.com/blog/grant-send-and-read-sms-permissions-on-android")) + startActivity(intent) + } + } else { + button.visibility = View.GONE + } + updatePermissionLayoutVisibility() + } + + private fun updatePermissionLayoutVisibility() { + val smsButton = findViewById(R.id.smsPermissionButton) + val batteryButton = findViewById(R.id.batteryOptimizationButtonButton) + val layout = findViewById(R.id.batteryOptimizationLinearLayout) + + if (smsButton.visibility == View.GONE && batteryButton.visibility == View.GONE) { layout.visibility = View.GONE + } else { + layout.visibility = View.VISIBLE } } @@ -307,7 +348,6 @@ class MainActivity : AppCompatActivity() { val progressBar = findViewById(R.id.mainProgressIndicator) progressBar.visibility = View.VISIBLE - val liveData = MutableLiveData() liveData.observe(this) { exception -> run { @@ -316,34 +356,33 @@ class MainActivity : AppCompatActivity() { if (exception != null) { Timber.w("heartbeat sending failed with [$exception]") - Toast.makeText(context, exception, Toast.LENGTH_SHORT).show() + Toast.makeText(context, exception, Toast.LENGTH_LONG).show() return@run } - Toast.makeText(context, "Heartbeat Sent", Toast.LENGTH_SHORT).show() + Toast.makeText(context, "Heartbeat sent successfully", Toast.LENGTH_SHORT).show() setLastHeartbeatTimestamp(this) } } Thread { - var charging = Settings.isCharging(applicationContext) + val charging = Settings.isCharging(applicationContext) var error: String? = null try { - HttpSmsApiService.create(context).storeHeartbeat(Settings.getSIM1PhoneNumber(context), charging) + val phoneNumbers = mutableListOf() + phoneNumbers.add(Settings.getSIM1PhoneNumber(applicationContext)) + if (Settings.getActiveStatus(applicationContext, Constants.SIM2)) { + phoneNumbers.add(Settings.getSIM2PhoneNumber(applicationContext)) + } + val isStored = HttpSmsApiService.create(context).storeHeartbeat(phoneNumbers.toTypedArray(), charging) + if (!isStored) { + error = "Could not send heartbeat make sure the phone is connected to the internet" + } Settings.setHeartbeatTimestampAsync(applicationContext, System.currentTimeMillis()) } catch (exception: Exception) { Timber.e(exception) error = exception.javaClass.simpleName } - if (Settings.isDualSIM(context)) { - try { - HttpSmsApiService.create(context).storeHeartbeat(Settings.getSIM2PhoneNumber(context), charging) - Settings.setHeartbeatTimestampAsync(applicationContext, System.currentTimeMillis()) - } catch (exception: Exception) { - Timber.e(exception) - error = exception.javaClass.simpleName - } - } liveData.postValue(error) Timber.d("finished sending pulse") }.start() diff --git a/android/app/src/main/java/com/httpsms/Models.kt b/android/app/src/main/java/com/httpsms/Models.kt index 757bdee3..b4bf5464 100644 --- a/android/app/src/main/java/com/httpsms/Models.kt +++ b/android/app/src/main/java/com/httpsms/Models.kt @@ -53,6 +53,8 @@ data class Message ( @Json(name = "received_at") val receivedAt: String?, + val encrypted: Boolean, + @Json(name = "request_received_at") val requestReceivedAt: String, @@ -66,5 +68,24 @@ data class Message ( val type: String, @Json(name = "updated_at") - val updatedAt: String + val updatedAt: String, + + val attachments: List? = null +) + +data class ReceivedAttachment( + val name: String, + @Json(name = "content_type") + val contentType: String, + val content: String +) + +data class ReceivedMessageRequest( + val sim: String, + val from: String, + val to: String, + val content: String, + val encrypted: Boolean, + val timestamp: String, + val attachments: List? = null ) diff --git a/android/app/src/main/java/com/httpsms/ReceivedReceiver.kt b/android/app/src/main/java/com/httpsms/ReceivedReceiver.kt index 68816cad..3edc30e2 100644 --- a/android/app/src/main/java/com/httpsms/ReceivedReceiver.kt +++ b/android/app/src/main/java/com/httpsms/ReceivedReceiver.kt @@ -4,7 +4,7 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.provider.Telephony -import androidx.work.BackoffPolicy +import android.util.Base64 import androidx.work.Constraints import androidx.work.Data import androidx.work.NetworkType @@ -13,20 +13,30 @@ import androidx.work.WorkManager import androidx.work.Worker import androidx.work.WorkerParameters import androidx.work.workDataOf +import com.google.android.mms.pdu_alt.CharacterSets +import com.google.android.mms.pdu_alt.MultimediaMessagePdu +import com.google.android.mms.pdu_alt.PduParser +import com.google.android.mms.pdu_alt.RetrieveConf import timber.log.Timber +import java.io.File +import java.io.FileOutputStream import java.time.ZoneOffset import java.time.ZonedDateTime import java.time.format.DateTimeFormatter -import java.util.concurrent.TimeUnit class ReceivedReceiver: BroadcastReceiver() { - override fun onReceive(context: Context,intent: Intent) { - if (intent.action != Telephony.Sms.Intents.SMS_RECEIVED_ACTION) { + override fun onReceive(context: Context, intent: Intent) { + if (intent.action == Telephony.Sms.Intents.SMS_RECEIVED_ACTION) { + handleSmsReceived(context, intent) + } else if (intent.action == Telephony.Sms.Intents.WAP_PUSH_RECEIVED_ACTION) { + handleMmsReceived(context, intent) + } else { Timber.e("received invalid intent with action [${intent.action}]") - return } + } + private fun handleSmsReceived(context: Context, intent: Intent) { var smsSender = "" var smsBody = "" @@ -35,12 +45,7 @@ class ReceivedReceiver: BroadcastReceiver() smsBody += smsMessage.messageBody } - var sim = Constants.SIM1 - var owner = Settings.getSIM1PhoneNumber(context) - if (intent.getIntExtra("android.telephony.extra.SLOT_INDEX", 0) > 0 && Settings.isDualSIM(context)) { - owner = Settings.getSIM2PhoneNumber(context) - sim = Constants.SIM2 - } + val (sim, owner) = getSimAndOwner(context, intent) if (!Settings.isIncomingMessageEnabled(context, sim)) { Timber.w("[${sim}] is not active for incoming messages") @@ -56,7 +61,71 @@ class ReceivedReceiver: BroadcastReceiver() ) } - private fun handleMessageReceived(context: Context, sim: String, from: String, to : String, content: String) { + private fun handleMmsReceived(context: Context, intent: Intent) { + val pushData = intent.getByteArrayExtra("data") ?: return + val pdu = PduParser(pushData, true).parse() ?: return + + if (pdu !is MultimediaMessagePdu) { + Timber.d("Received PDU is not a MultimediaMessagePdu, ignoring.") + return + } + + val from = pdu.from?.string ?: "" + var content = "" + val attachmentFiles = mutableListOf() + + // Check if it's a RetrieveConf (which contains the actual message body) + if (pdu is RetrieveConf) { + val body = pdu.body + if (body != null) { + for (i in 0 until body.partsNum) { + val part = body.getPart(i) + val partData = part.data ?: continue + val contentType = String(part.contentType ?: "application/octet-stream".toByteArray()) + + if (contentType.startsWith("text/plain")) { + content += String(partData, charset(CharacterSets.getMimeName(part.charset))) + } else { + // Save attachment to a temporary file + val fileName = String(part.name ?: part.contentLocation ?: part.contentId ?: "attachment_$i".toByteArray()) + val tempFile = File(context.cacheDir, "received_mms_${System.currentTimeMillis()}_$i") + FileOutputStream(tempFile).use { it.write(partData) } + attachmentFiles.add("${tempFile.absolutePath}|${contentType}|${fileName}") + } + } + } + } else { + Timber.d("Received PDU is of type [${pdu.javaClass.simpleName}], body extraction not implemented.") + } + + val (sim, owner) = getSimAndOwner(context, intent) + + if (!Settings.isIncomingMessageEnabled(context, sim)) { + Timber.w("[${sim}] is not active for incoming messages") + return + } + + handleMessageReceived( + context, + sim, + from, + owner, + content, + attachmentFiles.toTypedArray() + ) + } + + private fun getSimAndOwner(context: Context, intent: Intent): Pair { + var sim = Constants.SIM1 + var owner = Settings.getSIM1PhoneNumber(context) + if (intent.getIntExtra("android.telephony.extra.SLOT_INDEX", 0) > 0 && Settings.isDualSIM(context)) { + owner = Settings.getSIM2PhoneNumber(context) + sim = Constants.SIM2 + } + return Pair(sim, owner) + } + + private fun handleMessageReceived(context: Context, sim: String, from: String, to : String, content: String, attachments: Array? = null) { val timestamp = ZonedDateTime.now(ZoneOffset.UTC) if (!Settings.isLoggedIn(context)) { @@ -69,6 +138,11 @@ class ReceivedReceiver: BroadcastReceiver() return } + var body = content; + if (Settings.encryptReceivedMessages(context)) { + body = Encrypter.encrypt(Settings.getEncryptionKey(context)!!, content) + } + val constraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .build() @@ -77,8 +151,10 @@ class ReceivedReceiver: BroadcastReceiver() Constants.KEY_MESSAGE_FROM to from, Constants.KEY_MESSAGE_TO to to, Constants.KEY_MESSAGE_SIM to sim, - Constants.KEY_MESSAGE_CONTENT to content, - Constants.KEY_MESSAGE_TIMESTAMP to DateTimeFormatter.ofPattern(Constants.TIMESTAMP_PATTERN).format(timestamp).replace("+", "Z") + Constants.KEY_MESSAGE_CONTENT to body, + Constants.KEY_MESSAGE_ENCRYPTED to Settings.encryptReceivedMessages(context), + Constants.KEY_MESSAGE_TIMESTAMP to DateTimeFormatter.ofPattern(Constants.TIMESTAMP_PATTERN).format(timestamp).replace("+", "Z"), + Constants.KEY_MESSAGE_ATTACHMENTS to attachments ) val work = OneTimeWorkRequest @@ -98,13 +174,52 @@ class ReceivedReceiver: BroadcastReceiver() override fun doWork(): Result { Timber.i("[${this.inputData.getString(Constants.KEY_MESSAGE_SIM)}] forwarding received message from [${this.inputData.getString(Constants.KEY_MESSAGE_FROM)}] to [${this.inputData.getString(Constants.KEY_MESSAGE_TO)}]") - if (HttpSmsApiService.create(applicationContext).receive( - this.inputData.getString(Constants.KEY_MESSAGE_SIM)!!, - this.inputData.getString(Constants.KEY_MESSAGE_FROM)!!, - this.inputData.getString(Constants.KEY_MESSAGE_TO)!!, - this.inputData.getString(Constants.KEY_MESSAGE_CONTENT)!!, - this.inputData.getString(Constants.KEY_MESSAGE_TIMESTAMP)!!, - )) { + val sim = this.inputData.getString(Constants.KEY_MESSAGE_SIM)!! + val from = this.inputData.getString(Constants.KEY_MESSAGE_FROM)!! + val to = this.inputData.getString(Constants.KEY_MESSAGE_TO)!! + val content = this.inputData.getString(Constants.KEY_MESSAGE_CONTENT)!! + val encrypted = this.inputData.getBoolean(Constants.KEY_MESSAGE_ENCRYPTED, false) + val timestamp = this.inputData.getString(Constants.KEY_MESSAGE_TIMESTAMP)!! + + val attachmentsData = inputData.getStringArray(Constants.KEY_MESSAGE_ATTACHMENTS) + val attachments = attachmentsData?.mapNotNull { + val parts = it.split("|") + val file = File(parts[0]) + if (file.exists()) { + val bytes = file.readBytes() + val base64Content = Base64.encodeToString(bytes, Base64.NO_WRAP) + ReceivedAttachment( + name = parts[2], + contentType = parts[1], + content = base64Content + ) + } else { + null + } + } + + val request = ReceivedMessageRequest( + sim = sim, + from = from, + to = to, + content = content, + encrypted = encrypted, + timestamp = timestamp, + attachments = attachments + ) + + val success = HttpSmsApiService.create(applicationContext).receive(request) + + // Cleanup temp files + attachmentsData?.forEach { + val path = it.split("|")[0] + val file = File(path) + if (file.exists()) { + file.delete() + } + } + + if (success) { return Result.success() } diff --git a/android/app/src/main/java/com/httpsms/Receiver.kt b/android/app/src/main/java/com/httpsms/Receiver.kt index 978a90eb..a54f363f 100644 --- a/android/app/src/main/java/com/httpsms/Receiver.kt +++ b/android/app/src/main/java/com/httpsms/Receiver.kt @@ -1,7 +1,10 @@ package com.httpsms import android.content.Context +import android.content.Context.RECEIVER_EXPORTED import android.content.IntentFilter +import android.os.Build +import androidx.annotation.RequiresApi import timber.log.Timber object Receiver { @@ -12,19 +15,35 @@ object Receiver { if(sentReceiver == null) { Timber.d("registering [sent] receiver for intent [${SmsManagerService.sentAction()}]") sentReceiver = SentReceiver() - context.registerReceiver( - sentReceiver, - IntentFilter(SmsManagerService.sentAction()) - ) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + context.registerReceiver( + sentReceiver, + IntentFilter(SmsManagerService.sentAction()), + RECEIVER_EXPORTED + ) + } else { + context.registerReceiver( + sentReceiver, + IntentFilter(SmsManagerService.sentAction()) + ) + } } if(deliveredReceiver == null) { Timber.d("registering [delivered] receiver for intent [${SmsManagerService.deliveredAction()}]") deliveredReceiver = DeliveredReceiver() - context.registerReceiver( - deliveredReceiver, - IntentFilter(SmsManagerService.deliveredAction()) - ) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + context.registerReceiver( + deliveredReceiver, + IntentFilter(SmsManagerService.deliveredAction()), + RECEIVER_EXPORTED + ) + } else { + context.registerReceiver( + deliveredReceiver, + IntentFilter(SmsManagerService.deliveredAction()) + ) + } } } diff --git a/android/app/src/main/java/com/httpsms/SentReceiver.kt b/android/app/src/main/java/com/httpsms/SentReceiver.kt index 878526bf..8786ba2c 100644 --- a/android/app/src/main/java/com/httpsms/SentReceiver.kt +++ b/android/app/src/main/java/com/httpsms/SentReceiver.kt @@ -14,12 +14,12 @@ import androidx.work.Worker import androidx.work.WorkerParameters import androidx.work.workDataOf import timber.log.Timber -import java.time.ZoneOffset -import java.time.ZonedDateTime -import java.time.format.DateTimeFormatter +import java.io.File internal class SentReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { + val messageId = intent.getStringExtra(Constants.KEY_MESSAGE_ID) + cleanupPduFile(context, messageId) when (resultCode) { Activity.RESULT_OK -> handleMessageSent(context, intent.getStringExtra(Constants.KEY_MESSAGE_ID)) SmsManager.RESULT_ERROR_GENERIC_FAILURE -> handleMessageFailed(context, intent.getStringExtra(Constants.KEY_MESSAGE_ID), "GENERIC_FAILURE") @@ -30,6 +30,26 @@ internal class SentReceiver : BroadcastReceiver() { } } + private fun cleanupPduFile(context: Context, messageId: String?) { + if (messageId == null) return + + try { + val baseMessageId = messageId.substringBefore(".") + val mmsDir = File(context.cacheDir, "mms_attachments") + val pduFile = File(mmsDir, "pdu_$baseMessageId.dat") + + if (pduFile.exists()) { + if (pduFile.delete()) { + Timber.d("Cleaned up PDU file for message ID [$baseMessageId]") + } else { + Timber.w("Failed to delete PDU file for message ID [$baseMessageId]") + } + } + } catch (e: Exception) { + Timber.e(e, "Error cleaning up PDU file for message ID [$messageId]") + } + } + private fun handleMessageSent(context: Context, messageId: String?) { if (!Receiver.isValid(context, messageId)) { return diff --git a/android/app/src/main/java/com/httpsms/Settings.kt b/android/app/src/main/java/com/httpsms/Settings.kt index 4c83c06e..8cda79da 100644 --- a/android/app/src/main/java/com/httpsms/Settings.kt +++ b/android/app/src/main/java/com/httpsms/Settings.kt @@ -16,12 +16,24 @@ object Settings { private const val SETTINGS_SIM2_ACTIVE = "SETTINGS_SIM2_ACTIVE_STATUS" private const val SETTINGS_SIM1_INCOMING_ACTIVE = "SETTINGS_SIM1_INCOMING_ACTIVE" private const val SETTINGS_SIM2_INCOMING_ACTIVE = "SETTINGS_SIM2_INCOMING_ACTIVE" + private const val SETTINGS_SIM1_INCOMING_CALL_ACTIVE = "SETTINGS_SIM1_INCOMING_CALL_ACTIVE" + private const val SETTINGS_SIM2_INCOMING_CALL_ACTIVE = "SETTINGS_SIM2_INCOMING_CALL_ACTIVE" + private const val SETTINGS_DEBUG_LOG_ENABLED = "SETTINGS_DEBUG_LOG_ENABLED" private const val SETTINGS_API_KEY = "SETTINGS_API_KEY" private const val SETTINGS_SERVER_URL = "SETTINGS_SERVER_URL" private const val SETTINGS_FCM_TOKEN = "SETTINGS_FCM_TOKEN" private const val SETTINGS_USER_ID = "SETTINGS_USER_ID" private const val SETTINGS_FCM_TOKEN_UPDATE_TIMESTAMP = "SETTINGS_FCM_TOKEN_UPDATE_TIMESTAMP" private const val SETTINGS_HEARTBEAT_TIMESTAMP = "SETTINGS_HEARTBEAT_TIMESTAMP" + private const val SETTINGS_ENCRYPTION_KEY = "SETTINGS_ENCRYPTION_KEY" + private const val SETTINGS_ENCRYPT_RECEIVED_MESSAGES = "SETTINGS_ENCRYPT_RECEIVED_MESSAGES" + + fun getPhoneNumber(context:Context, sim: String): String { + if (sim == Constants.SIM2) { + return getSIM2PhoneNumber(context) + } + return getSIM1PhoneNumber(context) + } fun getSIM1PhoneNumber(context: Context): String { Timber.d(Settings::getSIM1PhoneNumber.name) @@ -111,6 +123,49 @@ object Settings { return activeStatus } + fun isIncomingCallEventsEnabled(context: Context, sim: String): Boolean { + var setting = this.SETTINGS_SIM1_INCOMING_CALL_ACTIVE + if (sim == Constants.SIM2) { + setting = this.SETTINGS_SIM2_INCOMING_CALL_ACTIVE + } + val activeStatus = PreferenceManager + .getDefaultSharedPreferences(context) + .getBoolean(setting,false) + + Timber.d("SETTINGS_${sim}_INCOMING_CALL_ACTIVE: [$activeStatus]") + return activeStatus + } + + fun setIncomingCallEventsEnabled(context: Context, sim: String, enabled: Boolean) { + var setting = this.SETTINGS_SIM1_INCOMING_CALL_ACTIVE + if (sim == Constants.SIM2) { + setting = this.SETTINGS_SIM2_INCOMING_CALL_ACTIVE + } + + PreferenceManager.getDefaultSharedPreferences(context) + .edit() + .putBoolean(setting, enabled) + .apply() + } + + + fun isDebugLogEnabled(context: Context) : Boolean { + Timber.d(Settings::isDebugLogEnabled.name) + + return PreferenceManager + .getDefaultSharedPreferences(context) + .getBoolean(this.SETTINGS_DEBUG_LOG_ENABLED, false) + } + + fun setDebugLogEnabled(context: Context, status: Boolean) { + Timber.d(Settings::setDebugLogEnabled.name) + + PreferenceManager.getDefaultSharedPreferences(context) + .edit() + .putBoolean(this.SETTINGS_DEBUG_LOG_ENABLED, status) + .apply() + } + fun setIncomingActiveSIM1(context: Context, status: Boolean) { Timber.d(Settings::setIncomingActiveSIM1.name) @@ -120,6 +175,43 @@ object Settings { .apply() } + fun setEncryptReceivedMessages(context: Context, status: Boolean) { + Timber.d(Settings::setEncryptReceivedMessages.name) + + PreferenceManager.getDefaultSharedPreferences(context) + .edit() + .putBoolean(this.SETTINGS_ENCRYPT_RECEIVED_MESSAGES, status) + .apply() + } + + fun encryptReceivedMessages(context: Context): Boolean { + Timber.d(Settings::encryptReceivedMessages.name) + + val encryptReceivedMessages = PreferenceManager + .getDefaultSharedPreferences(context) + .getBoolean(this.SETTINGS_ENCRYPT_RECEIVED_MESSAGES,false) + + Timber.d("SETTINGS_ENCRYPT_RECEIVED_MESSAGES: [$encryptReceivedMessages]") + return encryptReceivedMessages && !getEncryptionKey(context).isNullOrEmpty() + } + + fun setEncryptionKey(context: Context, key: String?) { + Timber.d(Settings::setEncryptionKey.name) + + PreferenceManager.getDefaultSharedPreferences(context) + .edit() + .putString(this.SETTINGS_ENCRYPTION_KEY, key) + .apply() + } + + fun getEncryptionKey(context: Context): String? { + Timber.d(Settings::getEncryptionKey.name) + + return PreferenceManager + .getDefaultSharedPreferences(context) + .getString(this.SETTINGS_ENCRYPTION_KEY, "") + } + fun setIncomingActiveSIM2(context: Context, status: Boolean) { Timber.d(Settings::setIncomingActiveSIM2.name) diff --git a/android/app/src/main/java/com/httpsms/SettingsActivity.kt b/android/app/src/main/java/com/httpsms/SettingsActivity.kt index 948082e0..cbc2831b 100644 --- a/android/app/src/main/java/com/httpsms/SettingsActivity.kt +++ b/android/app/src/main/java/com/httpsms/SettingsActivity.kt @@ -4,6 +4,7 @@ import android.content.Context import android.content.Intent import android.os.Bundle import androidx.appcompat.app.AppCompatActivity +import androidx.core.widget.doAfterTextChanged import com.google.android.material.appbar.MaterialToolbar import com.google.android.material.button.MaterialButton import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -21,6 +22,11 @@ class SettingsActivity : AppCompatActivity() { } private fun fillSettings(context: Context) { + val debugLogs = findViewById(R.id.settingEnableDebugLogs) + debugLogs.isChecked = Settings.isDebugLogEnabled(context) + debugLogs.setOnCheckedChangeListener{ _, isChecked -> run { Settings.setDebugLogEnabled(context, isChecked) } } + + val phoneNumber = findViewById(R.id.settingsSIM1Input) phoneNumber.setText(Settings.getSIM1PhoneNumber(context)) phoneNumber.isEnabled = false @@ -41,20 +47,70 @@ class SettingsActivity : AppCompatActivity() { sim2Switch.visibility = SwitchMaterial.GONE val outgoingSwitch = findViewById(R.id.settings_sim2_outgoing_messages) outgoingSwitch.visibility = SwitchMaterial.GONE + } else { + val phoneNumberSIM2 = findViewById(R.id.settingsSIM2InputEdit) + phoneNumberSIM2.setText(Settings.getSIM2PhoneNumber(context)) + phoneNumberSIM2.isEnabled = false + + val sim2IncomingMessages = findViewById(R.id.settings_sim2_incoming_messages) + sim2IncomingMessages.isChecked = Settings.isIncomingMessageEnabled(context, Constants.SIM2) + sim2IncomingMessages.setOnCheckedChangeListener{ _, isChecked -> run { Settings.setIncomingActiveSIM2(context, isChecked) } } + + val sim2OutgoingMessages = findViewById(R.id.settings_sim2_outgoing_messages) + sim2OutgoingMessages.isChecked = Settings.getActiveStatus(context, Constants.SIM2) + sim2OutgoingMessages.setOnCheckedChangeListener{ _, isChecked -> run { Settings.setActiveStatusAsync(context, isChecked, Constants.SIM2) } } + } + + handleEncryptionSettings(context) + handleIncomingCallEvents(context) + } + + private fun handleIncomingCallEvents(context: Context) { + val enableIncomingCallEvents = findViewById(R.id.settingsSim1EnableIncomingCallEvents) + enableIncomingCallEvents.isChecked = Settings.isIncomingCallEventsEnabled(context, Constants.SIM1) + enableIncomingCallEvents.setOnCheckedChangeListener{ _, isChecked -> run { + Settings.setIncomingCallEventsEnabled(context, Constants.SIM1, isChecked) + }} + + val sim2IncomingCalls = findViewById(R.id.settingsSim2EnableIncomingCallEvents) + if (!Settings.isDualSIM(context)) { + sim2IncomingCalls.visibility = SwitchMaterial.GONE return } - val phoneNumberSIM2 = findViewById(R.id.settingsSIM2InputEdit) - phoneNumberSIM2.setText(Settings.getSIM2PhoneNumber(context)) - phoneNumberSIM2.isEnabled = false + sim2IncomingCalls.isChecked = Settings.isIncomingCallEventsEnabled(context, Constants.SIM2) + sim2IncomingCalls.setOnCheckedChangeListener{ _, isChecked -> run { + Settings.setIncomingCallEventsEnabled(context, Constants.SIM2, isChecked) + }} + } + + private fun handleEncryptionSettings(context: Context) { + val encryptionKey = findViewById(R.id.settingsEncryptionKeyInputEdit) + val encryptReceivedMessages = findViewById(R.id.settingsEncryptReceivedMessages) - val sim2IncomingMessages = findViewById(R.id.settings_sim2_incoming_messages) - sim2IncomingMessages.isChecked = Settings.isIncomingMessageEnabled(context, Constants.SIM2) - sim2IncomingMessages.setOnCheckedChangeListener{ _, isChecked -> run { Settings.setIncomingActiveSIM2(context, isChecked) } } + val key = Settings.getEncryptionKey(context) + if(key.isNullOrEmpty()) { + encryptReceivedMessages.isEnabled = false + } else { + encryptionKey.setText(key.trim()) + } - val sim2OutgoingMessages = findViewById(R.id.settings_sim2_outgoing_messages) - sim2OutgoingMessages.isChecked = Settings.getActiveStatus(context, Constants.SIM2) - sim2OutgoingMessages.setOnCheckedChangeListener{ _, isChecked -> run { Settings.setActiveStatusAsync(context, isChecked, Constants.SIM2) } } + encryptionKey.doAfterTextChanged{ + if (it == null || it.toString().isEmpty()) { + Settings.setEncryptionKey(context, null) + Settings.setEncryptReceivedMessages(context, false) + encryptReceivedMessages.isChecked = false + encryptReceivedMessages.isEnabled = false + } else { + encryptReceivedMessages.isEnabled = true + Settings.setEncryptionKey(context, it.toString().trim()) + } + } + + encryptReceivedMessages.isChecked = Settings.encryptReceivedMessages(context) + encryptReceivedMessages.setOnCheckedChangeListener{ _, isChecked -> run { + Settings.setEncryptReceivedMessages(context, isChecked) + }} } private fun registerListeners() { @@ -67,7 +123,6 @@ class SettingsActivity : AppCompatActivity() { redirectToMain() } - private fun redirectToMain() { finish() val switchActivityIntent = Intent(this, MainActivity::class.java) @@ -94,7 +149,11 @@ class SettingsActivity : AppCompatActivity() { Settings.setIncomingActiveSIM1(this, true) Settings.setIncomingActiveSIM2(this, true) Settings.setUserID(this, null) + Settings.setEncryptionKey(this, null) + Settings.setEncryptReceivedMessages(this, false) Settings.setFcmTokenLastUpdateTimestampAsync(this, 0) + Settings.setIncomingCallEventsEnabled(this, Constants.SIM1, false) + Settings.setIncomingCallEventsEnabled(this, Constants.SIM2, false) redirectToLogin() } .show() diff --git a/android/app/src/main/java/com/httpsms/SmsManagerService.kt b/android/app/src/main/java/com/httpsms/SmsManagerService.kt index 5407fac4..5f7ce6f5 100644 --- a/android/app/src/main/java/com/httpsms/SmsManagerService.kt +++ b/android/app/src/main/java/com/httpsms/SmsManagerService.kt @@ -36,7 +36,7 @@ class SmsManagerService { } else { context.getSystemService(SubscriptionManager::class.java) } - return localSubscriptionManager.activeSubscriptionInfoList.size > 1 + return localSubscriptionManager.activeSubscriptionInfoList!!.size > 1 } } @@ -54,17 +54,18 @@ class SmsManagerService { @Suppress("DEPRECATION") @SuppressLint("MissingPermission") - private fun getSmsManager(context: Context, sim: String = "DEFAULT"): SmsManager { + private fun getSmsManager(context: Context, sim: String = Constants.SIM1): SmsManager { val localSubscriptionManager: SubscriptionManager = if (Build.VERSION.SDK_INT < 31) { SubscriptionManager.from(context) } else { context.getSystemService(SubscriptionManager::class.java) } - val subscriptionId = if (sim == "SIM1" && localSubscriptionManager.activeSubscriptionInfoList.size > 0) { - localSubscriptionManager.activeSubscriptionInfoList[0].subscriptionId - } else if (sim == "SIM2" && localSubscriptionManager.activeSubscriptionInfoList.size > 1) { - localSubscriptionManager.activeSubscriptionInfoList[1].subscriptionId + Timber.d("active subscription info size: [${localSubscriptionManager.activeSubscriptionInfoList!!.size}]") + val subscriptionId = if (sim == Constants.SIM1 && localSubscriptionManager.activeSubscriptionInfoList!!.size > 0) { + localSubscriptionManager.activeSubscriptionInfoList!![0].subscriptionId + } else if (sim == Constants.SIM2 && localSubscriptionManager.activeSubscriptionInfoList!!.size > 1) { + localSubscriptionManager.activeSubscriptionInfoList!![1].subscriptionId } else{ SubscriptionManager.getDefaultSmsSubscriptionId() } @@ -75,4 +76,10 @@ class SmsManagerService { context.getSystemService(SmsManager::class.java).createForSubscriptionId(subscriptionId) } } + + // Wrapper for the smsManager's sendMultimediaMessage + fun sendMultimediaMessage(context: Context, pduUri: android.net.Uri, sim: String, sentIntent: PendingIntent) { + val smsManager = getSmsManager(context, sim) + smsManager.sendMultimediaMessage(context, pduUri, null, null, sentIntent) + } } diff --git a/android/app/src/main/java/com/httpsms/receivers/PhoneStateReceiver.kt b/android/app/src/main/java/com/httpsms/receivers/PhoneStateReceiver.kt new file mode 100644 index 00000000..98ea8780 --- /dev/null +++ b/android/app/src/main/java/com/httpsms/receivers/PhoneStateReceiver.kt @@ -0,0 +1,174 @@ +package com.httpsms.receivers + +import android.annotation.SuppressLint +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.os.Build +import android.provider.CallLog +import android.telephony.SubscriptionManager +import android.telephony.TelephonyManager +import androidx.work.Constraints +import androidx.work.Data +import androidx.work.NetworkType +import androidx.work.OneTimeWorkRequest +import androidx.work.WorkManager +import androidx.work.Worker +import androidx.work.WorkerParameters +import androidx.work.workDataOf +import com.httpsms.Constants +import com.httpsms.HttpSmsApiService +import com.httpsms.Settings +import timber.log.Timber +import java.time.Instant +import java.time.ZoneOffset +import java.time.ZonedDateTime +import java.time.format.DateTimeFormatter + + +class PhoneStateReceiver : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + Timber.d("onReceive: ${intent.action}") + val stateStr = intent.extras!!.getString(TelephonyManager.EXTRA_STATE) + + @Suppress("DEPRECATION") + val number = intent.extras!!.getString(TelephonyManager.EXTRA_INCOMING_NUMBER) + if (stateStr != "IDLE" || number == null) { + Timber.d("event is not a missed call or permission is not granted state = [${stateStr}]") + return + } + + // Sleep so that the call gets added into the call log + Thread.sleep(200) + + val lastCall = getCallLog(context, number) + if (lastCall == null) { + Timber.d("The call from [${number}] was not a missed call.") + return + } + + handleMissedCallEvent(context, number, lastCall) + } + + private fun handleMissedCallEvent(context: Context, contact: String, callLog: Pair) { + val (timestamp, sim) = callLog + val owner = Settings.getPhoneNumber(context, sim) + + if (!Settings.isLoggedIn(context)) { + Timber.w("[${sim}] user is not logged in") + return + } + + if (!Settings.isIncomingCallEventsEnabled(context, callLog.second)) { + Timber.w("[${sim}] incoming call events is not enabled") + return + } + + val constraints = Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build() + + val inputData: Data = workDataOf( + Constants.KEY_MESSAGE_FROM to contact, + Constants.KEY_MESSAGE_SIM to sim, + Constants.KEY_MESSAGE_TO to owner, + Constants.KEY_MESSAGE_TIMESTAMP to DateTimeFormatter.ofPattern(Constants.TIMESTAMP_PATTERN).format(timestamp).replace("+", "Z") + ) + + val work = OneTimeWorkRequest + .Builder(MissedCallWorker::class.java) + .setConstraints(constraints) + .setInputData(inputData) + .build() + + WorkManager + .getInstance(context) + .enqueue(work) + + Timber.d("work enqueued with ID [${work.id}] for missed phone call from [${contact}] to [${owner}] in [${sim}]") + } + + @SuppressLint("MissingPermission") + private fun getSlotIndexFromSubscriptionId(context: Context, subscriptionId: Int): String { + val localSubscriptionManager: SubscriptionManager = if (Build.VERSION.SDK_INT < 31) { + @Suppress("DEPRECATION") + SubscriptionManager.from(context) + } else { + context.getSystemService(SubscriptionManager::class.java) + } + + var sim = Constants.SIM1 + localSubscriptionManager.activeSubscriptionInfoList!!.forEach { + if (it.subscriptionId == subscriptionId) { + if (it.simSlotIndex > 0){ + sim = Constants.SIM2 + } + } + } + return sim + } + + private fun getCallLog(context: Context, phoneNumber: String): Pair? { + // Specify the columns you want to retrieve from the call log + val projection = arrayOf(CallLog.Calls.NUMBER, CallLog.Calls.DATE, CallLog.Calls.TYPE, CallLog.Calls.PHONE_ACCOUNT_ID) + + // Query the call log content provider + val cursor = context.contentResolver.query( + CallLog.Calls.CONTENT_URI, + projection, + null, + null, + CallLog.Calls.DATE + " DESC" // Order by date in descending order + ) + + // Check if the cursor is not null and contains at least one entry + if (cursor != null && cursor.moveToFirst()) { + val number = cursor.getString(cursor.getColumnIndexOrThrow(CallLog.Calls.NUMBER)) + if (number != phoneNumber) { + Timber.w("last phone call has phone number [${number}] but the expected phone number was [${phoneNumber}]") + return null + } + + if (cursor.getInt(cursor.getColumnIndexOrThrow(CallLog.Calls.TYPE)) != CallLog.Calls.MISSED_TYPE) { + Timber.w("last phone call from phone number was [${phoneNumber}] was not a missed call") + return null + } + + val date = cursor.getLong(cursor.getColumnIndexOrThrow(CallLog.Calls.DATE)) + val sim = getSlotIndexFromSubscriptionId(context, cursor.getInt(cursor.getColumnIndexOrThrow(CallLog.Calls.PHONE_ACCOUNT_ID))) + + // Convert date to a readable format (optional) + val dateString = java.text.DateFormat.getDateTimeInstance().format(date) + Timber.d("missed call date is [${dateString}], SIM = [${sim}]") + + // Close the cursor to free up resources + cursor.close() + + // Construct a string representing the last call + return Pair(ZonedDateTime.ofInstant(Instant.ofEpochMilli(date), ZoneOffset.UTC), sim) + } + + // Close the cursor if it's not null even if it doesn't contain any data + cursor?.close() + + // Return null if no calls are found + return null + } + + internal class MissedCallWorker(appContext: Context, workerParams: WorkerParameters) : Worker(appContext, workerParams) { + override fun doWork(): Result { + Timber.i("[${this.inputData.getString(Constants.KEY_MESSAGE_SIM)}] forwarding missed call from [${this.inputData.getString(Constants.KEY_MESSAGE_FROM)}] to [${this.inputData.getString(Constants.KEY_MESSAGE_TO)}]") + + if (HttpSmsApiService.create(applicationContext).sendMissedCallEvent( + this.inputData.getString(Constants.KEY_MESSAGE_SIM)!!, + this.inputData.getString(Constants.KEY_MESSAGE_FROM)!!, + this.inputData.getString(Constants.KEY_MESSAGE_TO)!!, + this.inputData.getString(Constants.KEY_MESSAGE_TIMESTAMP)!!, + )) { + return Result.success() + } + + return Result.retry() + } + } +} diff --git a/android/app/src/main/java/com/httpsms/validators/PhoneNumberValidator.kt b/android/app/src/main/java/com/httpsms/validators/PhoneNumberValidator.kt new file mode 100644 index 00000000..7e9d2c42 --- /dev/null +++ b/android/app/src/main/java/com/httpsms/validators/PhoneNumberValidator.kt @@ -0,0 +1,37 @@ +package com.httpsms.validators + +import com.google.i18n.phonenumbers.PhoneNumberUtil +import timber.log.Timber + +class PhoneNumberValidator { + companion object { + private val phoneNumberUtil = PhoneNumberUtil.getInstance() + fun isValidPhoneNumber(phoneNumber: String, countryCode: String): Boolean { + Timber.e(countryCode) + return try { + if (phoneNumber.isEmpty()) { + return false + } + val number = phoneNumberUtil.parse(fixNumber(phoneNumber), countryCode) + phoneNumberUtil.isValidNumber(number) + } catch (e: Exception) { + false + } + } + fun formatE164(phoneNumber: String, countryCode: String): String { + return try { + val number = phoneNumberUtil.parse(fixNumber(phoneNumber), countryCode) + phoneNumberUtil.format(number, PhoneNumberUtil.PhoneNumberFormat.E164) + } catch (e: Exception) { + phoneNumber + } + } + + private fun fixNumber(phoneNumber: String): String { + if (phoneNumber.length >= 11 && !phoneNumber.startsWith("+")) { + return "+${phoneNumber}" + } + return phoneNumber + } + } +} diff --git a/android/app/src/main/java/com/httpsms/worker/HeartbeatWorker.kt b/android/app/src/main/java/com/httpsms/worker/HeartbeatWorker.kt index 1ac9cb4c..174f2742 100644 --- a/android/app/src/main/java/com/httpsms/worker/HeartbeatWorker.kt +++ b/android/app/src/main/java/com/httpsms/worker/HeartbeatWorker.kt @@ -16,37 +16,30 @@ class HeartbeatWorker(appContext: Context, workerParams: WorkerParameters) : Wor return Result.failure() } - sendSIM1Heartbeat() - if (Settings.isDualSIM(applicationContext)) { - sendSIM2Heartbeat() + val phoneNumbers = mutableListOf() + if (Settings.getActiveStatus(applicationContext, Constants.SIM1)) { + phoneNumbers.add(Settings.getSIM1PhoneNumber(applicationContext)) + } + if (Settings.getActiveStatus(applicationContext, Constants.SIM2)) { + phoneNumbers.add(Settings.getSIM2PhoneNumber(applicationContext)) } - return Result.success() - } - - private fun sendSIM1Heartbeat() { - if (!Settings.getActiveStatus(applicationContext, Constants.SIM1)) { - Timber.w("[SIM1] user is not active, stopping processing") - return + if (phoneNumbers.isEmpty()) { + Timber.w("both [SIM1] and [SIM2] are inactive stopping processing.") + return Result.success() } - HttpSmsApiService.create(applicationContext).storeHeartbeat(Settings.getSIM1PhoneNumber(applicationContext), Settings.isCharging(applicationContext)) - Timber.d("[SIM1] finished sending heartbeat to server") + try{ + HttpSmsApiService.create(applicationContext).storeHeartbeat(phoneNumbers.toTypedArray(), Settings.isCharging(applicationContext)) + Timber.d("finished sending heartbeats to server") - Settings.setHeartbeatTimestampAsync(applicationContext, System.currentTimeMillis()) - Timber.d("[SIM1] set the heartbeat timestamp") - } - - private fun sendSIM2Heartbeat() { - if (!Settings.getActiveStatus(applicationContext, Constants.SIM2)) { - Timber.w("[SIM2] user is not active, stopping processing") - return + Settings.setHeartbeatTimestampAsync(applicationContext, System.currentTimeMillis()) + Timber.d("Set the heartbeat timestamp") + } catch (exception: Exception) { + Timber.e(exception, "Failed to send [${phoneNumbers.joinToString()}] heartbeats to server") + return Result.failure() } - HttpSmsApiService.create(applicationContext).storeHeartbeat(Settings.getSIM2PhoneNumber(applicationContext), Settings.isCharging(applicationContext)) - Timber.d("[SIM2] finished sending heartbeat to server") - - Settings.setHeartbeatTimestampAsync(applicationContext, System.currentTimeMillis()) - Timber.d("[SIM2] set the heartbeat timestamp") + return Result.success() } } diff --git a/android/app/src/main/res/drawable/ic_launcher_foreground.xml b/android/app/src/main/res/drawable/ic_launcher_foreground.xml index dd939e93..b9f0f461 100644 --- a/android/app/src/main/res/drawable/ic_launcher_foreground.xml +++ b/android/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -1,12 +1,12 @@ - + android:viewportWidth="287.92" + android:viewportHeight="275.73"> + diff --git a/android/app/src/main/res/drawable/open_in_new_24.xml b/android/app/src/main/res/drawable/open_in_new_24.xml new file mode 100644 index 00000000..b257c344 --- /dev/null +++ b/android/app/src/main/res/drawable/open_in_new_24.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/app/src/main/res/layout/activity_login.xml b/android/app/src/main/res/layout/activity_login.xml index 0a1f046c..03552be5 100644 --- a/android/app/src/main/res/layout/activity_login.xml +++ b/android/app/src/main/res/layout/activity_login.xml @@ -1,167 +1,188 @@ - - - - - - - + + - - + + + + - - + android:layout_marginTop="32dp" + android:layout_marginBottom="24dp" + android:autoLink="web" + android:lineHeight="28sp" + android:text="@string/get_your_api_key" + android:textAlignment="center" + android:textSize="20sp" + app:layout_constraintBottom_toTopOf="@+id/loginApiKeyTextInputLayout" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/imageView" + app:layout_constraintVertical_bias="0" + app:layout_constraintVertical_chainStyle="packed" /> - - - + android:hint="@string/text_area_api_key" + app:errorEnabled="true" + app:endIconMode="custom" + app:endIconDrawable="@android:drawable/ic_menu_camera" + app:endIconContentDescription="cameraButton" + app:layout_constraintBottom_toTopOf="@+id/loginPhoneNumberLayoutSIM1" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/textView"> + + - + - - - + android:layout_marginTop="8dp" + android:hint="@string/login_phone_number_sim1" + app:errorEnabled="true" + app:placeholderText="@string/login_phone_number_hint" + app:layout_constraintBottom_toTopOf="@+id/loginPhoneNumberLayoutSIM2" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/loginApiKeyTextInputLayout"> - + + + - + app:placeholderText="@string/login_phone_number_hint" + app:layout_constraintBottom_toTopOf="@+id/loginServerUrlLayoutContainer" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/loginPhoneNumberLayoutSIM1"> - - - - - - + + + + + + + + - - - + android:layout_marginTop="16dp" + android:orientation="vertical" + android:gravity="center" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintEnd_toEndOf="parent" + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/loginServerUrlLayoutContainer"> + + + + + + + + diff --git a/android/app/src/main/res/layout/activity_main.xml b/android/app/src/main/res/layout/activity_main.xml index d04b9150..75849475 100644 --- a/android/app/src/main/res/layout/activity_main.xml +++ b/android/app/src/main/res/layout/activity_main.xml @@ -3,9 +3,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:paddingLeft="16dp" - android:paddingRight="16dp" android:layout_height="match_parent" + android:fitsSystemWindows="true" tools:context=".MainActivity"> - - @@ -46,8 +37,6 @@ android:orientation="vertical" android:padding="16dp"> - - - @@ -96,8 +86,6 @@ android:orientation="vertical" android:padding="16dp"> - - - + + + app:indicatorColor="@color/pink_500" /> + + + android:layout_height="match_parent" + android:fitsSystemWindows="true"> - - + + + android:layout_marginTop="16dp" + android:orientation="vertical" + android:paddingLeft="16dp" + android:paddingRight="16dp"> - + android:hint="@string/settings_sim1" + app:errorEnabled="true" + android:layout_marginTop="16dp" + tools:layout_editor_absoluteX="16dp"> - + - - - - - + - + android:layout_marginBottom="16dp" + android:minHeight="48dp" + android:text="@string/settings_outgoing_messages_sim1" + android:textSize="18sp" + tools:ignore="TouchTargetSizeCheck" /> + + - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 00000000..5ed0a2df --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 00000000..5ed0a2df --- /dev/null +++ b/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index 05099b2e..00000000 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp new file mode 100644 index 00000000..51365e72 Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png deleted file mode 100644 index b3729383..00000000 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp new file mode 100644 index 00000000..908082cb Binary files /dev/null and b/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png deleted file mode 100644 index 454eeacd..00000000 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp new file mode 100644 index 00000000..f082a496 Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png deleted file mode 100644 index f9401e85..00000000 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp new file mode 100644 index 00000000..13e28fff Binary files /dev/null and b/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png deleted file mode 100644 index bc131057..00000000 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp new file mode 100644 index 00000000..94922c81 Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png deleted file mode 100644 index 1b25d02c..00000000 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..a5eb961b Binary files /dev/null and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 788f854a..00000000 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp new file mode 100644 index 00000000..247689de Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png deleted file mode 100644 index 8b192e58..00000000 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..0ea1c127 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png deleted file mode 100644 index 05e89ad8..00000000 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp new file mode 100644 index 00000000..4f469b61 Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png deleted file mode 100644 index 45bb8ec1..00000000 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png and /dev/null differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp new file mode 100644 index 00000000..65563f8f Binary files /dev/null and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/android/app/src/main/res/values-night/themes.xml b/android/app/src/main/res/values-night/themes.xml index ab0774cd..dd456a18 100644 --- a/android/app/src/main/res/values-night/themes.xml +++ b/android/app/src/main/res/values-night/themes.xml @@ -12,5 +12,6 @@ #121212 + false diff --git a/android/app/src/main/res/values/ic_launcher_background.xml b/android/app/src/main/res/values/ic_launcher_background.xml index 7201bbff..81b4d761 100644 --- a/android/app/src/main/res/values/ic_launcher_background.xml +++ b/android/app/src/main/res/values/ic_launcher_background.xml @@ -1,4 +1,4 @@ - #121212 + #000000 diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index a3083e47..02dfa1b6 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -7,7 +7,7 @@ Login With API Key API Key HTTP Sms Logo - Open\nhttpsms.com/settings\nto get your API key + Get Your API Key at\nhttpsms.com/settings Log Out e.g +18005550199 (international format) e.g https://api.httpsms.com @@ -17,13 +17,19 @@ https://api.httpsms.com httpsms.com - %s Disable Battery Optimization + Enable SMS Permission App Settings SIM1 SIM2 - Enable Outgoing Messages (SIM1) - Enable Incoming Messages (SIM1) - Enable Incoming Messages (SIM2) - Enable Outgoing Messages (SIM2) - Phone Number (SIM1) - Phone Number (SIM2) + Enable SIM1 outgoing messages + Enable SIM1 incoming messages + Enable SIM2 incoming messages + Enable SIM2 outgoing messages + Phone number (SIM1) + Phone number (SIM2) + Encryption Key + Encrypt received messages + Enable debug logs + Enable SIM1 missed call events + Enable SIM2 missed call events diff --git a/android/app/src/main/res/values/themes.xml b/android/app/src/main/res/values/themes.xml index 75429247..5914accc 100644 --- a/android/app/src/main/res/values/themes.xml +++ b/android/app/src/main/res/values/themes.xml @@ -12,6 +12,8 @@ #121212 + + false diff --git a/android/app/src/main/res/xml/file_paths.xml b/android/app/src/main/res/xml/file_paths.xml new file mode 100644 index 00000000..0df3af41 --- /dev/null +++ b/android/app/src/main/res/xml/file_paths.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/android/build.gradle b/android/build.gradle deleted file mode 100644 index aa9b130f..00000000 --- a/android/build.gradle +++ /dev/null @@ -1,27 +0,0 @@ -// Top-level build file where you can add configuration options common to all sub-projects/modules. -buildscript { - ext { - kotlin_version = '1.9.10' - } - repositories { - // Check that you have the following line (if not, add it): - google() - mavenCentral() // Google's Maven repository - - } - dependencies { - // Add this line - classpath 'com.google.gms:google-services:4.4.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -plugins { - id 'com.android.application' version '8.1.2' apply false - id 'com.android.library' version '8.1.2' apply false - id 'org.jetbrains.kotlin.android' version '1.6.21' apply false -} - -task clean(type: Delete) { - delete rootProject.buildDir -} diff --git a/android/build.gradle.kts b/android/build.gradle.kts new file mode 100644 index 00000000..32077a01 --- /dev/null +++ b/android/build.gradle.kts @@ -0,0 +1,19 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. +buildscript { + repositories { + google() + mavenCentral() + } + dependencies { + classpath("com.google.gms:google-services:4.4.2") + } +} + +plugins { + id("com.android.application") version "9.1.1" apply false + id("com.android.library") version "9.1.1" apply false +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/android/gradle.properties b/android/gradle.properties index cccbfe6f..1f124546 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -21,5 +21,12 @@ kotlin.code.style=official # resources declared in the library itself and none from the library's dependencies, # thereby reducing the size of the R class for that library android.nonTransitiveRClass=true -android.defaults.buildfeatures.buildconfig=true android.nonFinalResIds=false +android.defaults.buildfeatures.resvalues=true +android.sdk.defaultTargetSdkToCompileSdkIfUnset=false +android.enableAppCompileTimeRClass=false +android.usesSdkInManifest.disallowed=false +android.uniquePackageNames=false +android.dependency.useConstraints=true +android.r8.strictFullModeForKeepRules=false +android.r8.optimizedResourceShrinking=false diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index a8dc302a..2721b96b 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Thu Jun 23 15:32:32 EEST 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/android/settings.gradle b/android/settings.gradle.kts similarity index 95% rename from android/settings.gradle rename to android/settings.gradle.kts index baf72e29..75be430a 100644 --- a/android/settings.gradle +++ b/android/settings.gradle.kts @@ -13,4 +13,4 @@ dependencyResolutionManagement { } } rootProject.name = "httpSMS" -include ':app' +include(":app") diff --git a/api/.env.docker b/api/.env.docker new file mode 100644 index 00000000..5441d7fe --- /dev/null +++ b/api/.env.docker @@ -0,0 +1,67 @@ +ENV=production + +# This is the project-id of the firebase project you created in the setup instructions +GCP_PROJECT_ID=httpsms-docker + +USE_HTTP_LOGGER=true + +EVENTS_QUEUE_TYPE=emulator +EVENTS_QUEUE_NAME=events-local +EVENTS_QUEUE_ENDPOINT=http://localhost:8000/v1/events + +# This is the user API key for the system admin user that is used to authenticate requests to the /v1/events endpoint +# You need to create a system user in the `users` table in your database and put the API key and ID of this user here +EVENTS_QUEUE_USER_API_KEY=system-user-api-key +EVENTS_QUEUE_USER_ID=system-user-id + +# This is the actual conetnt of your service account firebase-credentials.json file that you downloaded in the setup instructions +# e.g FIREBASE_CREDENTIALS='{ "type": "service_account", "project_id": "httpsms-docker", "private_key_id":..... +FIREBASE_CREDENTIALS= + +# This is the from name for your emails +SMTP_FROM_NAME=httpSMS +# This is the address where your email messages should come from. You should make sure this matches what is configured on your SMTP service +SMTP_FROM_EMAIL=httpsms@local.com + +# These are the credentials for your SMTP email service +SMTP_USERNAME= +SMTP_PASSWORD= +SMTP_HOST=smtp.mailtrap.io +SMTP_PORT=2525 + +# This is the URL of the application UI and it's used to generate links in emails +APP_URL=http://localhost:3000 + +# The name of the application you can set it to whatever you like +APP_NAME=httpSMS + +# This is the port where the API server will run on +APP_PORT=8000 + +# Host for the swagger UI +SWAGGER_HOST=localhost:8000 + +# Postgresql Database connection string +DATABASE_URL=postgresql://dbusername:dbpassword@postgres:5432/httpsms +DATABASE_URL_DEDICATED=postgresql://dbusername:dbpassword@postgres:5432/httpsms + +# Redis connection string +REDIS_URL=redis://@redis:6379 + +# Google Cloud Storage bucket for MMS attachments. Leave empty to use in-memory storage. +GCS_BUCKET_NAME= + +# [optional] If you would like to use uptrace.dev for distributed tracing, you can set the DSN here. +# This is optional and you can leave it empty if you don't want to use uptrace +UPTRACE_DSN= + + +# [optional] Websocket configuration for https://pusher.com if you will like to frontend to update in real time +PUSHER_APP_ID= +PUSHER_KEY= +PUSHER_SECRET= +PUSHER_CLUSTER= + +# Cloudflare Turnstile secret key for validating captcha tokens on the /v1/messages/search route +# Get your secret key at https://developers.cloudflare.com/turnstile/get-started/ +CLOUDFLARE_TURNSTILE_SECRET_KEY= diff --git a/api/.gitignore b/api/.gitignore index 15a5f751..19bdf4df 100644 --- a/api/.gitignore +++ b/api/.gitignore @@ -1,5 +1,4 @@ -docs -.env +.env.local *main.exe tmp $path @@ -8,3 +7,5 @@ $path .flaskenv* !.env.project !.env.vault +!.env.docker +cmd/experiments/* diff --git a/api/Dockerfile b/api/Dockerfile index 43511a00..8e0206a2 100644 --- a/api/Dockerfile +++ b/api/Dockerfile @@ -1,4 +1,4 @@ -FROM golang as builder +FROM golang AS builder ARG GIT_COMMIT ENV GIT_COMMIT=$GIT_COMMIT @@ -15,9 +15,7 @@ COPY . . RUN go get github.com/swaggo/swag/gen@latest RUN go get github.com/swaggo/swag/cmd/swag@latest RUN go install github.com/swaggo/swag/cmd/swag -RUN swag init -RUN go install github.com/gobuffalo/packr/v2/packr2@latest -RUN packr2 +RUN swag init --requiredByDefault --parseDependency --parseInternal RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-X main.Version=$GIT_COMMIT" -o /bin/http-sms . diff --git a/api/README.md b/api/README.md deleted file mode 100644 index f0b61dac..00000000 --- a/api/README.md +++ /dev/null @@ -1,30 +0,0 @@ -# HTTP SMS API - -[![Go Report Card](https://goreportcard.com/badge/github.com/NdoleStudio/httpsms)](https://goreportcard.com/report/github.com/NdoleStudio/httpsms) -[![PkgGoDev](https://pkg.go.dev/badge/github.com/NdoleStudio/httpsms/api)](https://pkg.go.dev/github.com/NdoleStudio/httpsms/api) - -API Docs: https://api.httpsms.com/index.html - -## Building - -To build and run the API on your computer, you can use the command below. - -```bash -# Build the API -go go build -o main.exe; - -# Run the API -./main.exe -``` - -## Testing - -You can run the unit tests for this client from the root directory using the command below: - -```bash -go test -v -``` - -## License - -This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details diff --git a/api/cloudbuild.yaml b/api/cloudbuild.yaml index 6919a24e..3abc9e5d 100644 --- a/api/cloudbuild.yaml +++ b/api/cloudbuild.yaml @@ -1,5 +1,5 @@ steps: - - name: "gcr.io/kaniko-project/executor:latest" + - name: "gcr.io/kaniko-project/executor:v1.23.2" id: "Build image and push" dir: "api" args: @@ -7,8 +7,8 @@ steps: - "--destination=us.gcr.io/$PROJECT_ID/$_SERVICE_NAME:latest" - "--dockerfile=Dockerfile" - "--context=." - - "--build-arg=GIT_COMMIT=$COMMIT_SHA" - - "--snapshotMode=time" + - "--build-arg=GIT_COMMIT=$SHORT_SHA" + - "--snapshot-mode=time" - id: "Deploy to cloud run" name: "gcr.io/cloud-builders/gcloud" diff --git a/api/cmd/experiments/main.go b/api/cmd/experiments/main.go deleted file mode 100644 index 7ab7be23..00000000 --- a/api/cmd/experiments/main.go +++ /dev/null @@ -1,110 +0,0 @@ -package main - -import ( - "context" - "fmt" - "log" - "os" - "strings" - "sync" - "time" - - "github.com/palantir/stacktrace" - - "github.com/NdoleStudio/httpsms/pkg/di" - "github.com/NdoleStudio/httpsms/pkg/entities" - "github.com/google/uuid" - - "github.com/carlmjohnson/requests" - - "github.com/joho/godotenv" -) - -func main() { - err := godotenv.Load("../../.env") - if err != nil { - log.Fatal("Error loading .env file") - } - - container := di.NewLiteContainer() - logger := container.Logger() - - logger.Info("Starting experiments") -} - -func chunkBy[T any](items []T, chunkSize int) (chunks [][]T) { - for chunkSize < len(items) { - items, chunks = items[chunkSize:], append(chunks, items[0:chunkSize:chunkSize]) - } - return append(chunks, items) -} - -func deleteContacts(container *di.Container) { - sendgrid := container.MarketingService() - logger := container.Logger() - - b, err := os.ReadFile("28462979_cf6f5478-3e15-4666-95d7-59149df6f0fd.csv") // just pass the file name - if err != nil { - logger.Fatal(stacktrace.Propagate(err, "cannot read file")) - } - - lines := strings.Split(string(b), "\n")[1:] - var contacts []string - for _, line := range lines { - contacts = append(contacts, strings.ReplaceAll(strings.Split(line, ",")[17], "\"", "")) - } - - chunks := chunkBy(contacts, 100) - for _, chunk := range chunks { - err = sendgrid.DeleteContacts(context.Background(), chunk) - if err != nil { - logger.Fatal(err) - } - } -} - -func text3CX() { - container := di.NewLiteContainer() - repo := container.Integration3CXRepository() - - err := repo.Save(context.Background(), &entities.Integration3CX{ - ID: uuid.MustParse("b0b1acdc-69dd-4aee-8277-ba4adc5d2e90"), - UserID: "XtABz6zdeFMoBLoltz6SREDvRSh2", - WebhookURL: "https://lagomtest.3cx.com.au/sms/generic/155e125bf7874f8fae5adbcaac49fdf8", - CreatedAt: time.Now().UTC(), - UpdatedAt: time.Now().UTC(), - }) - if err != nil { - container.Logger().Fatal(err) - } -} - -func loadTest() { - wg := sync.WaitGroup{} - for i := 0; i < 1; i++ { - wg.Add(1) - go func(count int) { - sendSMS(fmt.Sprintf("[%d] In the quiet of the night, the stars above whisper secrets of the universe. We, mere stardust, seek meaning in their cosmic dance, yearning to unlock the mysteries of existence that stretch far beyond our earthly bounds.", count)) - wg.Done() - }(i) - - } - wg.Wait() -} - -func sendSMS(content string) { - var response string - err := requests.URL(os.Getenv("BASIC_URL")). - BodyJSON(map[string]any{ - "content": content, - "from": os.Getenv("BASIC_FROM"), - "to": os.Getenv("BASIC_TO"), - }). - BasicAuth(os.Getenv("BASIC_USERNAME"), os.Getenv("BASIC_PASSWORD")). - ToString(&response). - Fetch(context.Background()) - if err != nil { - log.Fatal(err) - } - log.Printf("%s\n", response) -} diff --git a/api/cmd/loadtest/main.go b/api/cmd/loadtest/main.go index 3e60d688..f072840c 100644 --- a/api/cmd/loadtest/main.go +++ b/api/cmd/loadtest/main.go @@ -2,6 +2,11 @@ package main import ( "context" + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "crypto/sha256" + "encoding/base64" "fmt" "log" "os" @@ -55,13 +60,12 @@ func sendSingle() { err := requests. URL("/v1/messages/send"). Host("api.httpsms.com"). - // Host("localhost:8000"). - // Scheme("http"). Header("x-api-key", os.Getenv("HTTPSMS_KEY")). - BodyJSON(&map[string]string{ - "content": fmt.Sprintf("[%s] In the quiet of the night, the stars above whisper secrets of the universe. We, mere stardust, seek meaning in their cosmic dance, yearning to unlock the mysteries of existence that stretch far beyond our earthly bounds.", time.Now()), + BodyJSON(&map[string]any{ + "content": fmt.Sprintf("This is a test text message [%d]", i), "from": os.Getenv("HTTPSMS_FROM"), "to": os.Getenv("HTTPSMS_TO"), + "encrypted": false, "request_id": fmt.Sprintf("load-%s-%d", uuid.NewString(), i), }). ToString(&responsePayload). @@ -72,3 +76,57 @@ func sendSingle() { log.Println(responsePayload) } } + +func encrypt(value string) string { + key := sha256.Sum256([]byte("Password123")) + iv := make([]byte, 16) + _, err := rand.Read(iv) + if err != nil { + log.Fatal(stacktrace.Propagate(err, "cannot generate iv")) + } + c := ase256(value, key[:], iv) + fmt.Println("iv", base64.StdEncoding.EncodeToString(iv)) + fmt.Println("cypher", base64.StdEncoding.EncodeToString(c)) + fmt.Println("cypher+iv", base64.StdEncoding.EncodeToString(append(iv, c...))) + return base64.StdEncoding.EncodeToString(append(iv, c...)) +} + +func decode(value string) string { + content, err := base64.StdEncoding.DecodeString(value) + if err != nil { + log.Fatal(err) + } + + key := sha256.Sum256([]byte(os.Getenv("HTTPSMS_ENCRYPTION_KEY"))) + iv := content[:16] + + return ase256Decode(content[16:], key[:], iv) +} + +func ase256(plaintext string, key []byte, bIV []byte) []byte { + block, err := aes.NewCipher(key) + if err != nil { + log.Fatal(err) + } + + text := []byte(plaintext) + + stream := cipher.NewCFBEncrypter(block, bIV) + cypher := make([]byte, len(text)) + stream.XORKeyStream(cypher, text) + + return cypher +} + +func ase256Decode(cipherText []byte, key []byte, iv []byte) (decryptedString string) { + // Create a new AES cipher with the key and encrypted message + block, err := aes.NewCipher(key) + if err != nil { + log.Fatal(err) + } + + // Decrypt the message + stream := cipher.NewCFBDecrypter(block, iv) + stream.XORKeyStream(cipherText, cipherText) + return string(cipherText) +} diff --git a/api/docs/docs.go b/api/docs/docs.go new file mode 100644 index 00000000..ab5a4ea6 --- /dev/null +++ b/api/docs/docs.go @@ -0,0 +1,4866 @@ +// Package docs Code generated by swaggo/swag. DO NOT EDIT +package docs + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "contact": { + "name": "support@httpsms.com", + "email": "support@httpsms.com" + }, + "license": { + "name": "AGPL-3.0", + "url": "https://raw.githubusercontent.com/NdoleStudio/http-sms-manager/main/LICENSE" + }, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/billing/usage": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get the summary of sent and received messages for a user in the current month", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Billing" + ], + "summary": "Get Billing Usage.", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.BillingUsageResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/billing/usage-history": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get billing usage records of sent and received messages for a user in the past. It will be sorted by timestamp in descending order.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Billing" + ], + "summary": "Get billing usage history.", + "parameters": [ + { + "minimum": 0, + "type": "integer", + "description": "number of heartbeats to skip", + "name": "skip", + "in": "query" + }, + { + "maximum": 100, + "minimum": 1, + "type": "integer", + "description": "number of heartbeats to return", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.BillingUsagesResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/bulk-messages": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Sends bulk SMS messages to multiple users based on our [CSV template](https://httpsms.com/templates/httpsms-bulk.csv) or our [Excel template](https://httpsms.com/templates/httpsms-bulk.xlsx).", + "consumes": [ + "multipart/form-data" + ], + "produces": [ + "application/json" + ], + "tags": [ + "BulkSMS" + ], + "summary": "Store bulk SMS file", + "parameters": [ + { + "type": "file", + "description": "The Excel or CSV file containing the messages to be sent.", + "name": "document", + "in": "formData", + "required": true + } + ], + "responses": { + "202": { + "description": "Accepted", + "schema": { + "$ref": "#/definitions/responses.NoContent" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/discord-integrations": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get the discord integrations of a user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "DiscordIntegration" + ], + "summary": "Get discord integrations of a user", + "parameters": [ + { + "minimum": 0, + "type": "integer", + "description": "number of discord integrations to skip", + "name": "skip", + "in": "query" + }, + { + "type": "string", + "description": "filter discord integrations containing query", + "name": "query", + "in": "query" + }, + { + "maximum": 20, + "minimum": 1, + "type": "integer", + "description": "number of discord integrations to return", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.DiscordsResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Store a discord integration for the authenticated user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "DiscordIntegration" + ], + "summary": "Store discord integration", + "parameters": [ + { + "description": "Payload of the discord integration request", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.DiscordStore" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/responses.DiscordResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/discord-integrations/{discordID}": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Update a discord integration for the currently authenticated user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "DiscordIntegration" + ], + "summary": "Update a discord integration", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the discord integration", + "name": "discordID", + "in": "path", + "required": true + }, + { + "description": "Payload of discord integration to update", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.DiscordUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.DiscordResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Delete a discord integration for a user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Webhooks" + ], + "summary": "Delete discord integration", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the discord integration", + "name": "discordID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/responses.NoContent" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/discord/event": { + "post": { + "description": "Publish a discord event to the registered listeners", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Discord" + ], + "summary": "Consume a discord event", + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/responses.NoContent" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/heartbeats": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get the last time a phone number requested for outstanding messages. It will be sorted by timestamp in descending order.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Heartbeats" + ], + "summary": "Get heartbeats of an owner phone number", + "parameters": [ + { + "type": "string", + "default": "+18005550199", + "description": "the owner's phone number", + "name": "owner", + "in": "query", + "required": true + }, + { + "minimum": 0, + "type": "integer", + "description": "number of heartbeats to skip", + "name": "skip", + "in": "query" + }, + { + "type": "string", + "description": "filter containing query", + "name": "query", + "in": "query" + }, + { + "maximum": 20, + "minimum": 1, + "type": "integer", + "description": "number of heartbeats to return", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.HeartbeatsResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Store the heartbeat to make notify that a phone number is still active", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Heartbeats" + ], + "summary": "Register heartbeat of an owner phone number", + "parameters": [ + { + "description": "Payload of the heartbeat request", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.HeartbeatStore" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.HeartbeatResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/integration/3cx/messages": { + "post": { + "description": "Sends an SMS message from the 3CX platform", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "3CXIntegration" + ], + "summary": "Sends a 3CX SMS message", + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/responses.NoContent" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/message-threads": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get list of contacts which a phone number has communicated with (threads). It will be sorted by timestamp in descending order.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "MessageThreads" + ], + "summary": "Get message threads for a phone number", + "parameters": [ + { + "type": "string", + "default": "+18005550199", + "description": "owner phone number", + "name": "owner", + "in": "query", + "required": true + }, + { + "minimum": 0, + "type": "integer", + "description": "number of messages to skip", + "name": "skip", + "in": "query" + }, + { + "type": "string", + "description": "filter message threads containing query", + "name": "query", + "in": "query" + }, + { + "maximum": 20, + "minimum": 1, + "type": "integer", + "description": "number of messages to return", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.MessageThreadsResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/message-threads/{messageThreadID}": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Updates the details of a message thread", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "MessageThreads" + ], + "summary": "Update a message thread", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the message thread", + "name": "messageThreadID", + "in": "path", + "required": true + }, + { + "description": "Payload of message thread details to update", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.MessageThreadUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.PhoneResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Delete a message thread from the database and also deletes all the messages in the thread.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "MessageThreads" + ], + "summary": "Delete a message thread from the database.", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the message thread", + "name": "messageThreadID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/responses.NoContent" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/responses.NotFound" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/messages": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get list of messages which are sent between 2 phone numbers. It will be sorted by timestamp in descending order.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Messages" + ], + "summary": "Get messages which are sent between 2 phone numbers", + "parameters": [ + { + "type": "string", + "default": "+18005550199", + "description": "the owner's phone number", + "name": "owner", + "in": "query", + "required": true + }, + { + "type": "string", + "default": "+18005550100", + "description": "the contact's phone number", + "name": "contact", + "in": "query", + "required": true + }, + { + "minimum": 0, + "type": "integer", + "description": "number of messages to skip", + "name": "skip", + "in": "query" + }, + { + "type": "string", + "description": "filter messages containing query", + "name": "query", + "in": "query" + }, + { + "maximum": 20, + "minimum": 1, + "type": "integer", + "description": "number of messages to return", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.MessagesResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/messages/bulk-send": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Add bulk SMS messages to be sent by the android phone", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Messages" + ], + "summary": "Send bulk SMS messages", + "parameters": [ + { + "description": "Bulk send message request payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.MessageBulkSend" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/responses.MessagesResponse" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/messages/calls/missed": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "This endpoint is called by the httpSMS android app to register a missed call event on the mobile phone.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Messages" + ], + "summary": "Register a missed call event on the mobile phone", + "parameters": [ + { + "description": "Payload of the missed call event.", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.MessageCallMissed" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.MessageResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/responses.NotFound" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/messages/outstanding": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get an outstanding message to be sent by an android phone", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Messages" + ], + "summary": "Get an outstanding message", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703cb", + "description": "The ID of the message", + "name": "message_id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.MessageResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/messages/receive": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Add a new message received from a mobile phone", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Messages" + ], + "summary": "Receive a new SMS message from a mobile phone", + "parameters": [ + { + "description": "Received message request payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.MessageReceive" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.MessageResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/messages/search": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "This returns the list of all messages based on the filter criteria including missed calls", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Messages" + ], + "summary": "Search all messages of a user", + "parameters": [ + { + "type": "string", + "description": "Cloudflare turnstile token https://www.cloudflare.com/en-gb/application-services/products/turnstile/", + "name": "token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "+18005550199,+18005550100", + "description": "the owner's phone numbers", + "name": "owners", + "in": "query", + "required": true + }, + { + "minimum": 0, + "type": "integer", + "description": "number of messages to skip", + "name": "skip", + "in": "query" + }, + { + "type": "string", + "description": "filter messages containing query", + "name": "query", + "in": "query" + }, + { + "maximum": 200, + "minimum": 1, + "type": "integer", + "description": "number of messages to return", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.MessagesResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/messages/send": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Add a new SMS message to be sent by your Android phone", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Messages" + ], + "summary": "Send an SMS message", + "parameters": [ + { + "description": "Send message request payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.MessageSend" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.MessageResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/messages/{messageID}": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get a message from the database by the message ID.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Messages" + ], + "summary": "Get a message from the database.", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the message", + "name": "messageID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/responses.MessageResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/responses.NotFound" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Delete a message from the database and removes the message content from the list of threads.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Messages" + ], + "summary": "Delete a message from the database.", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the message", + "name": "messageID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/responses.NoContent" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/responses.NotFound" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/messages/{messageID}/events": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Use this endpoint to send events for a message when it is failed, sent or delivered by the mobile phone.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Messages" + ], + "summary": "Upsert an event for a message on the mobile phone", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the message", + "name": "messageID", + "in": "path", + "required": true + }, + { + "description": "Payload of the event emitted.", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.MessageEvent" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.MessageResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/responses.NotFound" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/phone-api-keys": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get list phone API keys which a user has registered on the httpSMS application", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PhoneAPIKeys" + ], + "summary": "Get the phone API keys of a user", + "parameters": [ + { + "minimum": 0, + "type": "integer", + "description": "number of phone api keys to skip", + "name": "skip", + "in": "query" + }, + { + "type": "string", + "description": "filter phone api keys with name containing query", + "name": "query", + "in": "query" + }, + { + "maximum": 100, + "minimum": 1, + "type": "integer", + "description": "number of phone api keys to return", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.PhoneAPIKeysResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Creates a new phone API key which can be used to log in to the httpSMS app on your Android phone", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PhoneAPIKeys" + ], + "summary": "Store phone API key", + "parameters": [ + { + "description": "Payload of new phone API key.", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.PhoneAPIKeyStoreRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.PhoneAPIKeyResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/phone-api-keys/{phoneAPIKeyID}": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Delete a phone API Key from the database and cannot be used for authentication anymore.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PhoneAPIKeys" + ], + "summary": "Delete a phone API key from the database.", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the phone API key", + "name": "phoneAPIKeyID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/responses.NoContent" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/responses.NotFound" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/phone-api-keys/{phoneAPIKeyID}/phones/{phoneID}": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "You will need to login again to the httpSMS app on your Android phone with a new phone API key.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "PhoneAPIKeys" + ], + "summary": "Remove the association of a phone from the phone API key.", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the phone API key", + "name": "phoneAPIKeyID", + "in": "path", + "required": true + }, + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the phone", + "name": "phoneID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/responses.NoContent" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/responses.NotFound" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/phones": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get list of phones which a user has registered on the http sms application", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Phones" + ], + "summary": "Get phones of a user", + "parameters": [ + { + "minimum": 0, + "type": "integer", + "description": "number of heartbeats to skip", + "name": "skip", + "in": "query" + }, + { + "type": "string", + "description": "filter phones containing query", + "name": "query", + "in": "query" + }, + { + "maximum": 20, + "minimum": 1, + "type": "integer", + "description": "number of phones to return", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.PhonesResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Updates properties of a user's phone. If the phone with this number does not exist, a new one will be created. Think of this method like an 'upsert'", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Phones" + ], + "summary": "Upsert Phone", + "parameters": [ + { + "description": "Payload of new phone number.", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.PhoneUpsert" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.PhoneResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/phones/fcm-token": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Updates the FCM token of a phone. If the phone with this number does not exist, a new one will be created. Think of this method like an 'upsert'", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Phones" + ], + "summary": "Upserts the FCM token of a phone", + "parameters": [ + { + "description": "Payload of new FCM token.", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.PhoneFCMToken" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.PhoneResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/phones/{phoneID}": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Delete a phone that has been sored in the database", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Phones" + ], + "summary": "Delete Phone", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the phone", + "name": "phoneID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/responses.NoContent" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/users/me": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get details of the currently authenticated user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Get current user", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.UserResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Updates the details of the currently authenticated user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Update a user", + "parameters": [ + { + "description": "Payload of user details to update", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.UserUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.PhoneResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Deletes the currently authenticated user together with all their data.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Delete a user", + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/responses.NoContent" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/users/subscription": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Cancel the subscription of the authenticated user.", + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Cancel the user's subscription", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.NoContent" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/users/subscription-update-url": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Fetches the subscription URL of the authenticated user.", + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Currently authenticated user subscription update URL", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.OkString" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/users/subscription/invoices/{subscriptionInvoiceID}": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Generates a new invoice PDF file for the given subscription payment with given parameters.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/pdf" + ], + "tags": [ + "Users" + ], + "summary": "Generate a subscription payment invoice", + "parameters": [ + { + "description": "Generate subscription payment invoice parameters", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.UserPaymentInvoice" + } + }, + { + "type": "string", + "description": "ID of the subscription invoice to generate the PDF for", + "name": "subscriptionInvoiceID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "file" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/users/subscription/payments": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Subscription payments are generated throughout the lifecycle of a subscription, typically there is one at the time of purchase and then one for each renewal.", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Get the last 10 subscription payments.", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.UserSubscriptionPaymentsResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/users/{userID}/api-keys": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Rotate the user's API key in case the current API Key is compromised", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Rotate the user's API Key", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the user to update", + "name": "userID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.UserResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/users/{userID}/notifications": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Update the email notification settings for a user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Users" + ], + "summary": "Update notification settings", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the user to update", + "name": "userID", + "in": "path", + "required": true + }, + { + "description": "User notification details to update", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.UserNotificationUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.UserResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/v1/attachments/{userID}/{messageID}/{attachmentIndex}/{filename}": { + "get": { + "description": "Download an MMS attachment by its path components", + "produces": [ + "application/octet-stream" + ], + "tags": [ + "Attachments" + ], + "summary": "Download a message attachment", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "userID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Message ID", + "name": "messageID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Attachment index", + "name": "attachmentIndex", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Filename with extension", + "name": "filename", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "file" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/responses.NotFound" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/webhooks": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get the webhooks of a user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Webhooks" + ], + "summary": "Get webhooks of a user", + "parameters": [ + { + "minimum": 0, + "type": "integer", + "description": "number of webhooks to skip", + "name": "skip", + "in": "query" + }, + { + "type": "string", + "description": "filter webhooks containing query", + "name": "query", + "in": "query" + }, + { + "maximum": 20, + "minimum": 1, + "type": "integer", + "description": "number of webhooks to return", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.WebhooksResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Store a webhook for the authenticated user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Webhooks" + ], + "summary": "Store a webhook", + "parameters": [ + { + "description": "Payload of the webhook request", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.WebhookStore" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.WebhookResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/webhooks/{webhookID}": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Update a webhook for the currently authenticated user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Webhooks" + ], + "summary": "Update a webhook", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the webhook", + "name": "webhookID", + "in": "path", + "required": true + }, + { + "description": "Payload of webhook details to update", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.WebhookUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.WebhookResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Delete a webhook for a user", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "Webhooks" + ], + "summary": "Delete webhook", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the webhook", + "name": "webhookID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/responses.NoContent" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + } + }, + "definitions": { + "entities.BillingUsage": { + "type": "object", + "required": [ + "created_at", + "end_timestamp", + "id", + "received_messages", + "sent_messages", + "start_timestamp", + "total_cost", + "updated_at", + "user_id" + ], + "properties": { + "created_at": { + "type": "string", + "example": "2022-06-05T14:26:02.302718+03:00" + }, + "end_timestamp": { + "type": "string", + "example": "2022-01-31T23:59:59+00:00" + }, + "id": { + "type": "string", + "example": "32343a19-da5e-4b1b-a767-3298a73703cb" + }, + "received_messages": { + "type": "integer", + "example": 465 + }, + "sent_messages": { + "type": "integer", + "example": 321 + }, + "start_timestamp": { + "type": "string", + "example": "2022-01-01T00:00:00+00:00" + }, + "total_cost": { + "type": "integer", + "example": 0 + }, + "updated_at": { + "type": "string", + "example": "2022-06-05T14:26:10.303278+03:00" + }, + "user_id": { + "type": "string", + "example": "WB7DRDWrJZRGbYrv2CKGkqbzvqdC" + } + } + }, + "entities.Discord": { + "type": "object", + "required": [ + "created_at", + "id", + "incoming_channel_id", + "name", + "server_id", + "updated_at", + "user_id" + ], + "properties": { + "created_at": { + "type": "string", + "example": "2022-06-05T14:26:02.302718+03:00" + }, + "id": { + "type": "string", + "example": "32343a19-da5e-4b1b-a767-3298a73703cb" + }, + "incoming_channel_id": { + "type": "string", + "example": "1095780203256627291" + }, + "name": { + "type": "string", + "example": "Game Server" + }, + "server_id": { + "type": "string", + "example": "1095778291488653372" + }, + "updated_at": { + "type": "string", + "example": "2022-06-05T14:26:10.303278+03:00" + }, + "user_id": { + "type": "string", + "example": "WB7DRDWrJZRGbYrv2CKGkqbzvqdC" + } + } + }, + "entities.Heartbeat": { + "type": "object", + "required": [ + "charging", + "id", + "owner", + "timestamp", + "user_id", + "version" + ], + "properties": { + "charging": { + "type": "boolean", + "example": true + }, + "id": { + "type": "string", + "example": "32343a19-da5e-4b1b-a767-3298a73703cb" + }, + "owner": { + "type": "string", + "example": "+18005550199" + }, + "timestamp": { + "type": "string", + "example": "2022-06-05T14:26:01.520828+03:00" + }, + "user_id": { + "type": "string", + "example": "WB7DRDWrJZRGbYrv2CKGkqbzvqdC" + }, + "version": { + "type": "string", + "example": "344c10f" + } + } + }, + "entities.Message": { + "type": "object", + "required": [ + "attachments", + "contact", + "content", + "created_at", + "encrypted", + "id", + "max_send_attempts", + "order_timestamp", + "owner", + "request_received_at", + "send_attempt_count", + "sim", + "status", + "type", + "updated_at", + "user_id" + ], + "properties": { + "attachments": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "https://example.com/image.jpg", + "https://example.com/video.mp4" + ] + }, + "contact": { + "type": "string", + "example": "+18005550100" + }, + "content": { + "type": "string", + "example": "This is a sample text message" + }, + "created_at": { + "type": "string", + "example": "2022-06-05T14:26:02.302718+03:00" + }, + "delivered_at": { + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "encrypted": { + "type": "boolean", + "example": false + }, + "expired_at": { + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "failed_at": { + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "failure_reason": { + "type": "string", + "example": "UNKNOWN" + }, + "id": { + "type": "string", + "example": "32343a19-da5e-4b1b-a767-3298a73703cb" + }, + "last_attempted_at": { + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "max_send_attempts": { + "type": "integer", + "example": 1 + }, + "order_timestamp": { + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "owner": { + "type": "string", + "example": "+18005550199" + }, + "received_at": { + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "request_id": { + "type": "string", + "example": "153554b5-ae44-44a0-8f4f-7bbac5657ad4" + }, + "request_received_at": { + "type": "string", + "example": "2022-06-05T14:26:01.520828+03:00" + }, + "scheduled_at": { + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "scheduled_send_time": { + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "send_attempt_count": { + "type": "integer", + "example": 0 + }, + "send_time": { + "description": "SendDuration is the number of nanoseconds from when the request was received until when the mobile phone send the message", + "type": "integer", + "example": 133414 + }, + "sent_at": { + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "sim": { + "description": "SIM is the SIM card to use to send the message\n* SMS1: use the SIM card in slot 1\n* SMS2: use the SIM card in slot 2\n* DEFAULT: used the default communication SIM card", + "allOf": [ + { + "$ref": "#/definitions/entities.SIM" + } + ], + "example": "DEFAULT" + }, + "status": { + "type": "string", + "example": "pending" + }, + "type": { + "type": "string", + "example": "mobile-terminated" + }, + "updated_at": { + "type": "string", + "example": "2022-06-05T14:26:10.303278+03:00" + }, + "user_id": { + "type": "string", + "example": "WB7DRDWrJZRGbYrv2CKGkqbzvqdC" + } + } + }, + "entities.MessageThread": { + "type": "object", + "required": [ + "color", + "contact", + "created_at", + "id", + "is_archived", + "last_message_content", + "last_message_id", + "order_timestamp", + "owner", + "status", + "updated_at", + "user_id" + ], + "properties": { + "color": { + "type": "string", + "example": "indigo" + }, + "contact": { + "type": "string", + "example": "+18005550100" + }, + "created_at": { + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "id": { + "type": "string", + "example": "32343a19-da5e-4b1b-a767-3298a73703ca" + }, + "is_archived": { + "type": "boolean", + "example": false + }, + "last_message_content": { + "type": "string", + "example": "This is a sample message content" + }, + "last_message_id": { + "type": "string", + "example": "32343a19-da5e-4b1b-a767-3298a73703ca" + }, + "order_timestamp": { + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "owner": { + "type": "string", + "example": "+18005550199" + }, + "status": { + "type": "string", + "example": "PENDING" + }, + "updated_at": { + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "user_id": { + "type": "string", + "example": "WB7DRDWrJZRGbYrv2CKGkqbzvqdC" + } + } + }, + "entities.Phone": { + "type": "object", + "required": [ + "created_at", + "id", + "max_send_attempts", + "message_expiration_seconds", + "messages_per_minute", + "phone_number", + "sim", + "updated_at", + "user_id" + ], + "properties": { + "created_at": { + "type": "string", + "example": "2022-06-05T14:26:02.302718+03:00" + }, + "fcm_token": { + "type": "string", + "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzd....." + }, + "id": { + "type": "string", + "example": "32343a19-da5e-4b1b-a767-3298a73703cb" + }, + "max_send_attempts": { + "description": "MaxSendAttempts determines how many times to retry sending an SMS message", + "type": "integer", + "example": 2 + }, + "message_expiration_seconds": { + "description": "MessageExpirationSeconds is the duration in seconds after sending a message when it is considered to be expired.", + "type": "integer" + }, + "messages_per_minute": { + "type": "integer", + "example": 1 + }, + "missed_call_auto_reply": { + "type": "string", + "example": "This phone cannot receive calls. Please send an SMS instead." + }, + "phone_number": { + "type": "string", + "example": "+18005550199" + }, + "sim": { + "$ref": "#/definitions/entities.SIM" + }, + "updated_at": { + "type": "string", + "example": "2022-06-05T14:26:10.303278+03:00" + }, + "user_id": { + "type": "string", + "example": "WB7DRDWrJZRGbYrv2CKGkqbzvqdC" + } + } + }, + "entities.PhoneAPIKey": { + "type": "object", + "required": [ + "api_key", + "created_at", + "id", + "name", + "phone_ids", + "phone_numbers", + "updated_at", + "user_email", + "user_id" + ], + "properties": { + "api_key": { + "type": "string", + "example": "pk_DGW8NwQp7mxKaSZ72Xq9v6xxxxx" + }, + "created_at": { + "type": "string", + "example": "2022-06-05T14:26:02.302718+03:00" + }, + "id": { + "type": "string", + "example": "32343a19-da5e-4b1b-a767-3298a73703cb" + }, + "name": { + "type": "string", + "example": "Business Phone Key" + }, + "phone_ids": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "32343a19-da5e-4b1b-a767-3298a73703cb", + "32343a19-da5e-4b1b-a767-3298a73703cc" + ] + }, + "phone_numbers": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "+18005550199", + "+18005550100" + ] + }, + "updated_at": { + "type": "string", + "example": "2022-06-05T14:26:02.302718+03:00" + }, + "user_email": { + "type": "string", + "example": "user@gmail.com" + }, + "user_id": { + "type": "string", + "example": "WB7DRDWrJZRGbYrv2CKGkqbzvqdC" + } + } + }, + "entities.SIM": { + "type": "string", + "enum": [ + "SIM1", + "SIM2" + ], + "x-enum-varnames": [ + "SIM1", + "SIM2" + ] + }, + "entities.SubscriptionName": { + "type": "string", + "enum": [ + "free", + "pro-monthly", + "pro-yearly", + "ultra-monthly", + "ultra-yearly", + "pro-lifetime", + "20k-monthly", + "100k-monthly", + "50k-monthly", + "200k-monthly", + "20k-yearly" + ], + "x-enum-varnames": [ + "SubscriptionNameFree", + "SubscriptionNameProMonthly", + "SubscriptionNameProYearly", + "SubscriptionNameUltraMonthly", + "SubscriptionNameUltraYearly", + "SubscriptionNameProLifetime", + "SubscriptionName20KMonthly", + "SubscriptionName100KMonthly", + "SubscriptionName50KMonthly", + "SubscriptionName200KMonthly", + "SubscriptionName20KYearly" + ] + }, + "entities.User": { + "type": "object", + "required": [ + "api_key", + "created_at", + "email", + "id", + "notification_heartbeat_enabled", + "notification_message_status_enabled", + "notification_newsletter_enabled", + "notification_webhook_enabled", + "subscription_id", + "subscription_name", + "timezone", + "updated_at" + ], + "properties": { + "active_phone_id": { + "type": "string", + "example": "32343a19-da5e-4b1b-a767-3298a73703cb" + }, + "api_key": { + "type": "string", + "example": "x-api-key" + }, + "created_at": { + "type": "string", + "example": "2022-06-05T14:26:02.302718+03:00" + }, + "email": { + "type": "string", + "example": "name@email.com" + }, + "id": { + "type": "string", + "example": "WB7DRDWrJZRGbYrv2CKGkqbzvqdC" + }, + "notification_heartbeat_enabled": { + "type": "boolean", + "example": true + }, + "notification_message_status_enabled": { + "type": "boolean", + "example": true + }, + "notification_newsletter_enabled": { + "type": "boolean", + "example": true + }, + "notification_webhook_enabled": { + "type": "boolean", + "example": true + }, + "subscription_ends_at": { + "type": "string", + "example": "2022-06-05T14:26:02.302718+03:00" + }, + "subscription_id": { + "type": "string", + "example": "8f9c71b8-b84e-4417-8408-a62274f65a08" + }, + "subscription_name": { + "allOf": [ + { + "$ref": "#/definitions/entities.SubscriptionName" + } + ], + "example": "free" + }, + "subscription_renews_at": { + "type": "string", + "example": "2022-06-05T14:26:02.302718+03:00" + }, + "subscription_status": { + "type": "string", + "example": "on_trial" + }, + "timezone": { + "type": "string", + "example": "Europe/Helsinki" + }, + "updated_at": { + "type": "string", + "example": "2022-06-05T14:26:10.303278+03:00" + } + } + }, + "entities.Webhook": { + "type": "object", + "required": [ + "created_at", + "events", + "id", + "phone_numbers", + "signing_key", + "updated_at", + "url", + "user_id" + ], + "properties": { + "created_at": { + "type": "string", + "example": "2022-06-05T14:26:02.302718+03:00" + }, + "events": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "message.phone.received" + ] + }, + "id": { + "type": "string", + "example": "32343a19-da5e-4b1b-a767-3298a73703cb" + }, + "phone_numbers": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "+18005550199", + "+18005550100" + ] + }, + "signing_key": { + "type": "string", + "example": "DGW8NwQp7mxKaSZ72Xq9v67SLqSbWQvckzzmK8D6rvd7NywSEkdMJtuxKyEkYnCY" + }, + "updated_at": { + "type": "string", + "example": "2022-06-05T14:26:10.303278+03:00" + }, + "url": { + "type": "string", + "example": "https://example.com" + }, + "user_id": { + "type": "string", + "example": "WB7DRDWrJZRGbYrv2CKGkqbzvqdC" + } + } + }, + "requests.DiscordStore": { + "type": "object", + "required": [ + "incoming_channel_id", + "name", + "server_id" + ], + "properties": { + "incoming_channel_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "server_id": { + "type": "string" + } + } + }, + "requests.DiscordUpdate": { + "type": "object", + "required": [ + "incoming_channel_id", + "name", + "server_id" + ], + "properties": { + "incoming_channel_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "server_id": { + "type": "string" + } + } + }, + "requests.HeartbeatStore": { + "type": "object", + "required": [ + "charging", + "phone_numbers" + ], + "properties": { + "charging": { + "type": "boolean" + }, + "phone_numbers": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "requests.MessageAttachment": { + "type": "object", + "required": [ + "content", + "content_type", + "name" + ], + "properties": { + "content": { + "description": "Content is the base64-encoded attachment data", + "type": "string", + "example": "base64data..." + }, + "content_type": { + "description": "ContentType is the MIME type of the attachment", + "type": "string", + "example": "image/jpeg" + }, + "name": { + "description": "Name is the original filename of the attachment", + "type": "string", + "example": "photo.jpg" + } + } + }, + "requests.MessageBulkSend": { + "type": "object", + "required": [ + "content", + "from", + "to" + ], + "properties": { + "attachments": { + "description": "Attachments are optional. When you provide a list of attachments, the message will be sent out as an MMS", + "type": "array", + "items": { + "type": "string" + } + }, + "content": { + "type": "string", + "example": "This is a sample text message" + }, + "encrypted": { + "description": "Encrypted is used to determine if the content is end-to-end encrypted. Make sure to set the encryption key on the httpSMS mobile app", + "type": "boolean", + "example": false + }, + "from": { + "type": "string", + "example": "+18005550199" + }, + "request_id": { + "description": "RequestID is an optional parameter used to track a request from the client's perspective", + "type": "string", + "example": "153554b5-ae44-44a0-8f4f-7bbac5657ad4" + }, + "to": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "+18005550100", + "+18005550100" + ] + } + } + }, + "requests.MessageCallMissed": { + "type": "object", + "required": [ + "from", + "sim", + "timestamp", + "to" + ], + "properties": { + "from": { + "type": "string", + "example": "+18005550199" + }, + "sim": { + "type": "string", + "example": "SIM1" + }, + "timestamp": { + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "to": { + "type": "string", + "example": "+18005550100" + } + } + }, + "requests.MessageEvent": { + "type": "object", + "required": [ + "event_name", + "reason", + "timestamp" + ], + "properties": { + "event_name": { + "description": "EventName is the type of event\n* SENT: is emitted when a message is sent by the mobile phone\n* FAILED: is event is emitted when the message could not be sent by the mobile phone\n* DELIVERED: is event is emitted when a delivery report has been received by the mobile phone", + "type": "string", + "example": "SENT" + }, + "reason": { + "description": "Reason is the exact error message in case the event is an error", + "type": "string" + }, + "timestamp": { + "description": "Timestamp is the time when the event was emitted, Please send the timestamp in UTC with as much precision as possible", + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + } + } + }, + "requests.MessageReceive": { + "type": "object", + "required": [ + "content", + "encrypted", + "from", + "sim", + "timestamp", + "to" + ], + "properties": { + "attachments": { + "description": "Attachments is the list of MMS attachments received with the message", + "type": "array", + "items": { + "$ref": "#/definitions/requests.MessageAttachment" + } + }, + "content": { + "type": "string", + "example": "This is a sample text message received on a phone" + }, + "encrypted": { + "description": "Encrypted is used to determine if the content is end-to-end encrypted. Make sure to set the encryption key on the httpSMS mobile app", + "type": "boolean", + "example": false + }, + "from": { + "type": "string", + "example": "+18005550199" + }, + "sim": { + "description": "SIM card that received the message", + "allOf": [ + { + "$ref": "#/definitions/entities.SIM" + } + ], + "example": "SIM1" + }, + "timestamp": { + "description": "Timestamp is the time when the event was emitted, Please send the timestamp in UTC with as much precision as possible", + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "to": { + "type": "string", + "example": "+18005550100" + } + } + }, + "requests.MessageSend": { + "type": "object", + "required": [ + "content", + "from", + "to" + ], + "properties": { + "attachments": { + "description": "Attachments are optional. When you provide a list of attachments, the message will be sent out as an MMS", + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "https://example.com/image.jpg", + "https://example.com/video.mp4" + ] + }, + "content": { + "type": "string", + "example": "This is a sample text message" + }, + "encrypted": { + "description": "Encrypted is an optional parameter used to determine if the content is end-to-end encrypted. Make sure to set the encryption key on the httpSMS mobile app", + "type": "boolean", + "example": false + }, + "from": { + "type": "string", + "example": "+18005550199" + }, + "request_id": { + "description": "RequestID is an optional parameter used to track a request from the client's perspective", + "type": "string", + "example": "153554b5-ae44-44a0-8f4f-7bbac5657ad4" + }, + "send_at": { + "description": "SendAt is an optional parameter used to schedule a message to be sent in the future. The time is considered to be in your profile's local timezone and you can queue messages for up to 20 days (480 hours) in the future.", + "type": "string", + "example": "2025-12-19T16:39:57-08:00" + }, + "to": { + "type": "string", + "example": "+18005550100" + } + } + }, + "requests.MessageThreadUpdate": { + "type": "object", + "required": [ + "is_archived" + ], + "properties": { + "is_archived": { + "type": "boolean", + "example": true + } + } + }, + "requests.PhoneAPIKeyStoreRequest": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string", + "example": "My Phone API Key" + } + } + }, + "requests.PhoneFCMToken": { + "type": "object", + "required": [ + "fcm_token", + "phone_number", + "sim" + ], + "properties": { + "fcm_token": { + "type": "string", + "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzd....." + }, + "phone_number": { + "type": "string", + "example": "[+18005550199]" + }, + "sim": { + "description": "SIM is the SIM slot of the phone in case the phone has more than 1 SIM slot", + "type": "string", + "example": "SIM1" + } + } + }, + "requests.PhoneUpsert": { + "type": "object", + "required": [ + "fcm_token", + "max_send_attempts", + "message_expiration_seconds", + "messages_per_minute", + "missed_call_auto_reply", + "phone_number", + "sim" + ], + "properties": { + "fcm_token": { + "type": "string", + "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzd....." + }, + "max_send_attempts": { + "description": "MaxSendAttempts is the number of attempts when sending an SMS message to handle the case where the phone is offline.", + "type": "integer", + "example": 2 + }, + "message_expiration_seconds": { + "description": "MessageExpirationSeconds is the duration in seconds after sending a message when it is considered to be expired.", + "type": "integer", + "example": 12345 + }, + "messages_per_minute": { + "type": "integer", + "example": 1 + }, + "missed_call_auto_reply": { + "type": "string", + "example": "e.g. This phone cannot receive calls. Please send an SMS instead." + }, + "phone_number": { + "type": "string", + "example": "+18005550199" + }, + "sim": { + "description": "SIM is the SIM slot of the phone in case the phone has more than 1 SIM slot", + "type": "string", + "example": "SIM1" + } + } + }, + "requests.UserNotificationUpdate": { + "type": "object", + "required": [ + "heartbeat_enabled", + "message_status_enabled", + "newsletter_enabled", + "webhook_enabled" + ], + "properties": { + "heartbeat_enabled": { + "type": "boolean", + "example": true + }, + "message_status_enabled": { + "type": "boolean", + "example": true + }, + "newsletter_enabled": { + "type": "boolean", + "example": true + }, + "webhook_enabled": { + "type": "boolean", + "example": true + } + } + }, + "requests.UserPaymentInvoice": { + "type": "object", + "required": [ + "address", + "city", + "country", + "name", + "notes", + "state", + "zip_code" + ], + "properties": { + "address": { + "type": "string", + "example": "221B Baker Street, London" + }, + "city": { + "type": "string", + "example": "Los Angeles" + }, + "country": { + "type": "string", + "example": "US" + }, + "name": { + "type": "string", + "example": "Acme Corp" + }, + "notes": { + "type": "string", + "example": "Thank you for your business!" + }, + "state": { + "type": "string", + "example": "CA" + }, + "zip_code": { + "type": "string", + "example": "9800" + } + } + }, + "requests.UserUpdate": { + "type": "object", + "required": [ + "active_phone_id", + "timezone" + ], + "properties": { + "active_phone_id": { + "type": "string", + "example": "32343a19-da5e-4b1b-a767-3298a73703cb" + }, + "timezone": { + "type": "string", + "example": "Europe/Helsinki" + } + } + }, + "requests.WebhookStore": { + "type": "object", + "required": [ + "events", + "phone_numbers", + "signing_key", + "url" + ], + "properties": { + "events": { + "type": "array", + "items": { + "type": "string" + } + }, + "phone_numbers": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "+18005550100", + "+18005550100" + ] + }, + "signing_key": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "requests.WebhookUpdate": { + "type": "object", + "required": [ + "events", + "phone_numbers", + "signing_key", + "url" + ], + "properties": { + "events": { + "type": "array", + "items": { + "type": "string" + } + }, + "phone_numbers": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "+18005550100", + "+18005550100" + ] + }, + "signing_key": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "responses.BadRequest": { + "type": "object", + "required": [ + "data", + "message", + "status" + ], + "properties": { + "data": { + "type": "string", + "example": "The request body is not a valid JSON string" + }, + "message": { + "type": "string", + "example": "The request isn't properly formed" + }, + "status": { + "type": "string", + "example": "error" + } + } + }, + "responses.BillingUsageResponse": { + "type": "object", + "required": [ + "data", + "message", + "status" + ], + "properties": { + "data": { + "$ref": "#/definitions/entities.BillingUsage" + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.BillingUsagesResponse": { + "type": "object", + "required": [ + "data", + "message", + "status" + ], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/entities.BillingUsage" + } + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.DiscordResponse": { + "type": "object", + "required": [ + "data", + "message", + "status" + ], + "properties": { + "data": { + "$ref": "#/definitions/entities.Discord" + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.DiscordsResponse": { + "type": "object", + "required": [ + "data", + "message", + "status" + ], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/entities.Discord" + } + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.HeartbeatResponse": { + "type": "object", + "required": [ + "data", + "message", + "status" + ], + "properties": { + "data": { + "$ref": "#/definitions/entities.Heartbeat" + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.HeartbeatsResponse": { + "type": "object", + "required": [ + "data", + "message", + "status" + ], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/entities.Heartbeat" + } + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.InternalServerError": { + "type": "object", + "required": [ + "message", + "status" + ], + "properties": { + "message": { + "type": "string", + "example": "We ran into an internal error while handling the request." + }, + "status": { + "type": "string", + "example": "error" + } + } + }, + "responses.MessageResponse": { + "type": "object", + "required": [ + "data", + "message", + "status" + ], + "properties": { + "data": { + "$ref": "#/definitions/entities.Message" + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.MessageThreadsResponse": { + "type": "object", + "required": [ + "data", + "message", + "status" + ], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/entities.MessageThread" + } + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.MessagesResponse": { + "type": "object", + "required": [ + "data", + "message", + "status" + ], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/entities.Message" + } + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.NoContent": { + "type": "object", + "required": [ + "message", + "status" + ], + "properties": { + "message": { + "type": "string", + "example": "action performed successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.NotFound": { + "type": "object", + "required": [ + "message", + "status" + ], + "properties": { + "message": { + "type": "string", + "example": "cannot find message with ID [32343a19-da5e-4b1b-a767-3298a73703ca]" + }, + "status": { + "type": "string", + "example": "error" + } + } + }, + "responses.OkString": { + "type": "object", + "required": [ + "data", + "message", + "status" + ], + "properties": { + "data": { + "type": "string" + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.PhoneAPIKeyResponse": { + "type": "object", + "required": [ + "data", + "message", + "status" + ], + "properties": { + "data": { + "$ref": "#/definitions/entities.PhoneAPIKey" + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.PhoneAPIKeysResponse": { + "type": "object", + "required": [ + "data", + "message", + "status" + ], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/entities.PhoneAPIKey" + } + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.PhoneResponse": { + "type": "object", + "required": [ + "data", + "message", + "status" + ], + "properties": { + "data": { + "$ref": "#/definitions/entities.Phone" + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.PhonesResponse": { + "type": "object", + "required": [ + "data", + "message", + "status" + ], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/entities.Phone" + } + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.Unauthorized": { + "type": "object", + "required": [ + "data", + "message", + "status" + ], + "properties": { + "data": { + "type": "string", + "example": "Make sure your API key is set in the [X-API-Key] header in the request" + }, + "message": { + "type": "string", + "example": "You are not authorized to carry out this request." + }, + "status": { + "type": "string", + "example": "error" + } + } + }, + "responses.UnprocessableEntity": { + "type": "object", + "required": [ + "data", + "message", + "status" + ], + "properties": { + "data": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "message": { + "type": "string", + "example": "validation errors while handling request" + }, + "status": { + "type": "string", + "example": "error" + } + } + }, + "responses.UserResponse": { + "type": "object", + "required": [ + "data", + "message", + "status" + ], + "properties": { + "data": { + "$ref": "#/definitions/entities.User" + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.UserSubscriptionPaymentsResponse": { + "type": "object", + "required": [ + "data", + "message", + "status" + ], + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "required": [ + "attributes", + "id", + "type" + ], + "properties": { + "attributes": { + "type": "object", + "required": [ + "billing_reason", + "card_brand", + "card_last_four", + "created_at", + "currency", + "currency_rate", + "discount_total", + "discount_total_formatted", + "discount_total_usd", + "refunded", + "refunded_amount", + "refunded_amount_formatted", + "refunded_amount_usd", + "refunded_at", + "status", + "status_formatted", + "subtotal", + "subtotal_formatted", + "subtotal_usd", + "tax", + "tax_formatted", + "tax_inclusive", + "tax_usd", + "total", + "total_formatted", + "total_usd", + "updated_at" + ], + "properties": { + "billing_reason": { + "type": "string" + }, + "card_brand": { + "type": "string" + }, + "card_last_four": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "currency": { + "type": "string" + }, + "currency_rate": { + "type": "string" + }, + "discount_total": { + "type": "integer" + }, + "discount_total_formatted": { + "type": "string" + }, + "discount_total_usd": { + "type": "integer" + }, + "refunded": { + "type": "boolean" + }, + "refunded_amount": { + "type": "integer" + }, + "refunded_amount_formatted": { + "type": "string" + }, + "refunded_amount_usd": { + "type": "integer" + }, + "refunded_at": {}, + "status": { + "type": "string" + }, + "status_formatted": { + "type": "string" + }, + "subtotal": { + "type": "integer" + }, + "subtotal_formatted": { + "type": "string" + }, + "subtotal_usd": { + "type": "integer" + }, + "tax": { + "type": "integer" + }, + "tax_formatted": { + "type": "string" + }, + "tax_inclusive": { + "type": "boolean" + }, + "tax_usd": { + "type": "integer" + }, + "total": { + "type": "integer" + }, + "total_formatted": { + "type": "string" + }, + "total_usd": { + "type": "integer" + }, + "updated_at": { + "type": "string" + } + } + }, + "id": { + "type": "string" + }, + "type": { + "type": "string" + } + } + } + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.WebhookResponse": { + "type": "object", + "required": [ + "data", + "message", + "status" + ], + "properties": { + "data": { + "$ref": "#/definitions/entities.Webhook" + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.WebhooksResponse": { + "type": "object", + "required": [ + "data", + "message", + "status" + ], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/entities.Webhook" + } + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + } + }, + "securityDefinitions": { + "ApiKeyAuth": { + "type": "apiKey", + "name": "x-api-Key", + "in": "header" + } + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "1.0", + Host: "api.httpsms.com", + BasePath: "/v1", + Schemes: []string{"https"}, + Title: "httpSMS API Reference", + Description: "Use your Android phone to send and receive SMS messages via a simple programmable API with end-to-end encryption.", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/api/docs/swagger.json b/api/docs/swagger.json new file mode 100644 index 00000000..b8bc5739 --- /dev/null +++ b/api/docs/swagger.json @@ -0,0 +1,4393 @@ +{ + "schemes": ["https"], + "swagger": "2.0", + "info": { + "description": "Use your Android phone to send and receive SMS messages via a simple programmable API with end-to-end encryption.", + "title": "httpSMS API Reference", + "contact": { + "name": "support@httpsms.com", + "email": "support@httpsms.com" + }, + "license": { + "name": "AGPL-3.0", + "url": "https://raw.githubusercontent.com/NdoleStudio/http-sms-manager/main/LICENSE" + }, + "version": "1.0" + }, + "host": "api.httpsms.com", + "basePath": "/v1", + "paths": { + "/billing/usage": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get the summary of sent and received messages for a user in the current month", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Billing"], + "summary": "Get Billing Usage.", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.BillingUsageResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/billing/usage-history": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get billing usage records of sent and received messages for a user in the past. It will be sorted by timestamp in descending order.", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Billing"], + "summary": "Get billing usage history.", + "parameters": [ + { + "minimum": 0, + "type": "integer", + "description": "number of heartbeats to skip", + "name": "skip", + "in": "query" + }, + { + "maximum": 100, + "minimum": 1, + "type": "integer", + "description": "number of heartbeats to return", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.BillingUsagesResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/bulk-messages": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Sends bulk SMS messages to multiple users based on our [CSV template](https://httpsms.com/templates/httpsms-bulk.csv) or our [Excel template](https://httpsms.com/templates/httpsms-bulk.xlsx).", + "consumes": ["multipart/form-data"], + "produces": ["application/json"], + "tags": ["BulkSMS"], + "summary": "Store bulk SMS file", + "parameters": [ + { + "type": "file", + "description": "The Excel or CSV file containing the messages to be sent.", + "name": "document", + "in": "formData", + "required": true + } + ], + "responses": { + "202": { + "description": "Accepted", + "schema": { + "$ref": "#/definitions/responses.NoContent" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/discord-integrations": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get the discord integrations of a user", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["DiscordIntegration"], + "summary": "Get discord integrations of a user", + "parameters": [ + { + "minimum": 0, + "type": "integer", + "description": "number of discord integrations to skip", + "name": "skip", + "in": "query" + }, + { + "type": "string", + "description": "filter discord integrations containing query", + "name": "query", + "in": "query" + }, + { + "maximum": 20, + "minimum": 1, + "type": "integer", + "description": "number of discord integrations to return", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.DiscordsResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Store a discord integration for the authenticated user", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["DiscordIntegration"], + "summary": "Store discord integration", + "parameters": [ + { + "description": "Payload of the discord integration request", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.DiscordStore" + } + } + ], + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/responses.DiscordResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/discord-integrations/{discordID}": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Update a discord integration for the currently authenticated user", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["DiscordIntegration"], + "summary": "Update a discord integration", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the discord integration", + "name": "discordID", + "in": "path", + "required": true + }, + { + "description": "Payload of discord integration to update", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.DiscordUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.DiscordResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Delete a discord integration for a user", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Webhooks"], + "summary": "Delete discord integration", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the discord integration", + "name": "discordID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/responses.NoContent" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/discord/event": { + "post": { + "description": "Publish a discord event to the registered listeners", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Discord"], + "summary": "Consume a discord event", + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/responses.NoContent" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/heartbeats": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get the last time a phone number requested for outstanding messages. It will be sorted by timestamp in descending order.", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Heartbeats"], + "summary": "Get heartbeats of an owner phone number", + "parameters": [ + { + "type": "string", + "default": "+18005550199", + "description": "the owner's phone number", + "name": "owner", + "in": "query", + "required": true + }, + { + "minimum": 0, + "type": "integer", + "description": "number of heartbeats to skip", + "name": "skip", + "in": "query" + }, + { + "type": "string", + "description": "filter containing query", + "name": "query", + "in": "query" + }, + { + "maximum": 20, + "minimum": 1, + "type": "integer", + "description": "number of heartbeats to return", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.HeartbeatsResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Store the heartbeat to make notify that a phone number is still active", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Heartbeats"], + "summary": "Register heartbeat of an owner phone number", + "parameters": [ + { + "description": "Payload of the heartbeat request", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.HeartbeatStore" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.HeartbeatResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/integration/3cx/messages": { + "post": { + "description": "Sends an SMS message from the 3CX platform", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["3CXIntegration"], + "summary": "Sends a 3CX SMS message", + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/responses.NoContent" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/message-threads": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get list of contacts which a phone number has communicated with (threads). It will be sorted by timestamp in descending order.", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["MessageThreads"], + "summary": "Get message threads for a phone number", + "parameters": [ + { + "type": "string", + "default": "+18005550199", + "description": "owner phone number", + "name": "owner", + "in": "query", + "required": true + }, + { + "minimum": 0, + "type": "integer", + "description": "number of messages to skip", + "name": "skip", + "in": "query" + }, + { + "type": "string", + "description": "filter message threads containing query", + "name": "query", + "in": "query" + }, + { + "maximum": 20, + "minimum": 1, + "type": "integer", + "description": "number of messages to return", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.MessageThreadsResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/message-threads/{messageThreadID}": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Updates the details of a message thread", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["MessageThreads"], + "summary": "Update a message thread", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the message thread", + "name": "messageThreadID", + "in": "path", + "required": true + }, + { + "description": "Payload of message thread details to update", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.MessageThreadUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.PhoneResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Delete a message thread from the database and also deletes all the messages in the thread.", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["MessageThreads"], + "summary": "Delete a message thread from the database.", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the message thread", + "name": "messageThreadID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/responses.NoContent" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/responses.NotFound" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/messages": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get list of messages which are sent between 2 phone numbers. It will be sorted by timestamp in descending order.", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Messages"], + "summary": "Get messages which are sent between 2 phone numbers", + "parameters": [ + { + "type": "string", + "default": "+18005550199", + "description": "the owner's phone number", + "name": "owner", + "in": "query", + "required": true + }, + { + "type": "string", + "default": "+18005550100", + "description": "the contact's phone number", + "name": "contact", + "in": "query", + "required": true + }, + { + "minimum": 0, + "type": "integer", + "description": "number of messages to skip", + "name": "skip", + "in": "query" + }, + { + "type": "string", + "description": "filter messages containing query", + "name": "query", + "in": "query" + }, + { + "maximum": 20, + "minimum": 1, + "type": "integer", + "description": "number of messages to return", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.MessagesResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/messages/bulk-send": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Add bulk SMS messages to be sent by the android phone", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Messages"], + "summary": "Send bulk SMS messages", + "parameters": [ + { + "description": "Bulk send message request payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.MessageBulkSend" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/responses.MessagesResponse" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/messages/calls/missed": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "This endpoint is called by the httpSMS android app to register a missed call event on the mobile phone.", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Messages"], + "summary": "Register a missed call event on the mobile phone", + "parameters": [ + { + "description": "Payload of the missed call event.", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.MessageCallMissed" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.MessageResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/responses.NotFound" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/messages/outstanding": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get an outstanding message to be sent by an android phone", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Messages"], + "summary": "Get an outstanding message", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703cb", + "description": "The ID of the message", + "name": "message_id", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.MessageResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/messages/receive": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Add a new message received from a mobile phone", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Messages"], + "summary": "Receive a new SMS message from a mobile phone", + "parameters": [ + { + "description": "Received message request payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.MessageReceive" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.MessageResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/messages/search": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "This returns the list of all messages based on the filter criteria including missed calls", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Messages"], + "summary": "Search all messages of a user", + "parameters": [ + { + "type": "string", + "description": "Cloudflare turnstile token https://www.cloudflare.com/en-gb/application-services/products/turnstile/", + "name": "token", + "in": "header", + "required": true + }, + { + "type": "string", + "default": "+18005550199,+18005550100", + "description": "the owner's phone numbers", + "name": "owners", + "in": "query", + "required": true + }, + { + "minimum": 0, + "type": "integer", + "description": "number of messages to skip", + "name": "skip", + "in": "query" + }, + { + "type": "string", + "description": "filter messages containing query", + "name": "query", + "in": "query" + }, + { + "maximum": 200, + "minimum": 1, + "type": "integer", + "description": "number of messages to return", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.MessagesResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/messages/send": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Add a new SMS message to be sent by your Android phone", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Messages"], + "summary": "Send an SMS message", + "parameters": [ + { + "description": "Send message request payload", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.MessageSend" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.MessageResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/messages/{messageID}": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get a message from the database by the message ID.", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Messages"], + "summary": "Get a message from the database.", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the message", + "name": "messageID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/responses.MessageResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/responses.NotFound" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Delete a message from the database and removes the message content from the list of threads.", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Messages"], + "summary": "Delete a message from the database.", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the message", + "name": "messageID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/responses.NoContent" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/responses.NotFound" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/messages/{messageID}/events": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Use this endpoint to send events for a message when it is failed, sent or delivered by the mobile phone.", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Messages"], + "summary": "Upsert an event for a message on the mobile phone", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the message", + "name": "messageID", + "in": "path", + "required": true + }, + { + "description": "Payload of the event emitted.", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.MessageEvent" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.MessageResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/responses.NotFound" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/phone-api-keys": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get list phone API keys which a user has registered on the httpSMS application", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["PhoneAPIKeys"], + "summary": "Get the phone API keys of a user", + "parameters": [ + { + "minimum": 0, + "type": "integer", + "description": "number of phone api keys to skip", + "name": "skip", + "in": "query" + }, + { + "type": "string", + "description": "filter phone api keys with name containing query", + "name": "query", + "in": "query" + }, + { + "maximum": 100, + "minimum": 1, + "type": "integer", + "description": "number of phone api keys to return", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.PhoneAPIKeysResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Creates a new phone API key which can be used to log in to the httpSMS app on your Android phone", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["PhoneAPIKeys"], + "summary": "Store phone API key", + "parameters": [ + { + "description": "Payload of new phone API key.", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.PhoneAPIKeyStoreRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.PhoneAPIKeyResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/phone-api-keys/{phoneAPIKeyID}": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Delete a phone API Key from the database and cannot be used for authentication anymore.", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["PhoneAPIKeys"], + "summary": "Delete a phone API key from the database.", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the phone API key", + "name": "phoneAPIKeyID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/responses.NoContent" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/responses.NotFound" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/phone-api-keys/{phoneAPIKeyID}/phones/{phoneID}": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "You will need to login again to the httpSMS app on your Android phone with a new phone API key.", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["PhoneAPIKeys"], + "summary": "Remove the association of a phone from the phone API key.", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the phone API key", + "name": "phoneAPIKeyID", + "in": "path", + "required": true + }, + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the phone", + "name": "phoneID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/responses.NoContent" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/responses.NotFound" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/phones": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get list of phones which a user has registered on the http sms application", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Phones"], + "summary": "Get phones of a user", + "parameters": [ + { + "minimum": 0, + "type": "integer", + "description": "number of heartbeats to skip", + "name": "skip", + "in": "query" + }, + { + "type": "string", + "description": "filter phones containing query", + "name": "query", + "in": "query" + }, + { + "maximum": 20, + "minimum": 1, + "type": "integer", + "description": "number of phones to return", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.PhonesResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Updates properties of a user's phone. If the phone with this number does not exist, a new one will be created. Think of this method like an 'upsert'", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Phones"], + "summary": "Upsert Phone", + "parameters": [ + { + "description": "Payload of new phone number.", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.PhoneUpsert" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.PhoneResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/phones/fcm-token": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Updates the FCM token of a phone. If the phone with this number does not exist, a new one will be created. Think of this method like an 'upsert'", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Phones"], + "summary": "Upserts the FCM token of a phone", + "parameters": [ + { + "description": "Payload of new FCM token.", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.PhoneFCMToken" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.PhoneResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/phones/{phoneID}": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Delete a phone that has been sored in the database", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Phones"], + "summary": "Delete Phone", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the phone", + "name": "phoneID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/responses.NoContent" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/users/me": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get details of the currently authenticated user", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Get current user", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.UserResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + }, + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Updates the details of the currently authenticated user", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Update a user", + "parameters": [ + { + "description": "Payload of user details to update", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.UserUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.PhoneResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Deletes the currently authenticated user together with all their data.", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Delete a user", + "responses": { + "201": { + "description": "Created", + "schema": { + "$ref": "#/definitions/responses.NoContent" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/users/subscription": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Cancel the subscription of the authenticated user.", + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Cancel the user's subscription", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.NoContent" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/users/subscription-update-url": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Fetches the subscription URL of the authenticated user.", + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Currently authenticated user subscription update URL", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.OkString" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/users/subscription/invoices/{subscriptionInvoiceID}": { + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Generates a new invoice PDF file for the given subscription payment with given parameters.", + "consumes": ["application/json"], + "produces": ["application/pdf"], + "tags": ["Users"], + "summary": "Generate a subscription payment invoice", + "parameters": [ + { + "description": "Generate subscription payment invoice parameters", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.UserPaymentInvoice" + } + }, + { + "type": "string", + "description": "ID of the subscription invoice to generate the PDF for", + "name": "subscriptionInvoiceID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "file" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/users/subscription/payments": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Subscription payments are generated throughout the lifecycle of a subscription, typically there is one at the time of purchase and then one for each renewal.", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Get the last 10 subscription payments.", + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.UserSubscriptionPaymentsResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/users/{userID}/api-keys": { + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Rotate the user's API key in case the current API Key is compromised", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Rotate the user's API Key", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the user to update", + "name": "userID", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.UserResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/users/{userID}/notifications": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Update the email notification settings for a user", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Users"], + "summary": "Update notification settings", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the user to update", + "name": "userID", + "in": "path", + "required": true + }, + { + "description": "User notification details to update", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.UserNotificationUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.UserResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/v1/attachments/{userID}/{messageID}/{attachmentIndex}/{filename}": { + "get": { + "description": "Download an MMS attachment by its path components", + "produces": ["application/octet-stream"], + "tags": ["Attachments"], + "summary": "Download a message attachment", + "parameters": [ + { + "type": "string", + "description": "User ID", + "name": "userID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Message ID", + "name": "messageID", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Attachment index", + "name": "attachmentIndex", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Filename with extension", + "name": "filename", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "file" + } + }, + "404": { + "description": "Not Found", + "schema": { + "$ref": "#/definitions/responses.NotFound" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/webhooks": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Get the webhooks of a user", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Webhooks"], + "summary": "Get webhooks of a user", + "parameters": [ + { + "minimum": 0, + "type": "integer", + "description": "number of webhooks to skip", + "name": "skip", + "in": "query" + }, + { + "type": "string", + "description": "filter webhooks containing query", + "name": "query", + "in": "query" + }, + { + "maximum": 20, + "minimum": 1, + "type": "integer", + "description": "number of webhooks to return", + "name": "limit", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.WebhooksResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + }, + "post": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Store a webhook for the authenticated user", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Webhooks"], + "summary": "Store a webhook", + "parameters": [ + { + "description": "Payload of the webhook request", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.WebhookStore" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.WebhookResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + }, + "/webhooks/{webhookID}": { + "put": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Update a webhook for the currently authenticated user", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Webhooks"], + "summary": "Update a webhook", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the webhook", + "name": "webhookID", + "in": "path", + "required": true + }, + { + "description": "Payload of webhook details to update", + "name": "payload", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/requests.WebhookUpdate" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/responses.WebhookResponse" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + }, + "delete": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "Delete a webhook for a user", + "consumes": ["application/json"], + "produces": ["application/json"], + "tags": ["Webhooks"], + "summary": "Delete webhook", + "parameters": [ + { + "type": "string", + "default": "32343a19-da5e-4b1b-a767-3298a73703ca", + "description": "ID of the webhook", + "name": "webhookID", + "in": "path", + "required": true + } + ], + "responses": { + "204": { + "description": "No Content", + "schema": { + "$ref": "#/definitions/responses.NoContent" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/responses.BadRequest" + } + }, + "401": { + "description": "Unauthorized", + "schema": { + "$ref": "#/definitions/responses.Unauthorized" + } + }, + "422": { + "description": "Unprocessable Entity", + "schema": { + "$ref": "#/definitions/responses.UnprocessableEntity" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/responses.InternalServerError" + } + } + } + } + } + }, + "definitions": { + "entities.BillingUsage": { + "type": "object", + "required": [ + "created_at", + "end_timestamp", + "id", + "received_messages", + "sent_messages", + "start_timestamp", + "total_cost", + "updated_at", + "user_id" + ], + "properties": { + "created_at": { + "type": "string", + "example": "2022-06-05T14:26:02.302718+03:00" + }, + "end_timestamp": { + "type": "string", + "example": "2022-01-31T23:59:59+00:00" + }, + "id": { + "type": "string", + "example": "32343a19-da5e-4b1b-a767-3298a73703cb" + }, + "received_messages": { + "type": "integer", + "example": 465 + }, + "sent_messages": { + "type": "integer", + "example": 321 + }, + "start_timestamp": { + "type": "string", + "example": "2022-01-01T00:00:00+00:00" + }, + "total_cost": { + "type": "integer", + "example": 0 + }, + "updated_at": { + "type": "string", + "example": "2022-06-05T14:26:10.303278+03:00" + }, + "user_id": { + "type": "string", + "example": "WB7DRDWrJZRGbYrv2CKGkqbzvqdC" + } + } + }, + "entities.Discord": { + "type": "object", + "required": [ + "created_at", + "id", + "incoming_channel_id", + "name", + "server_id", + "updated_at", + "user_id" + ], + "properties": { + "created_at": { + "type": "string", + "example": "2022-06-05T14:26:02.302718+03:00" + }, + "id": { + "type": "string", + "example": "32343a19-da5e-4b1b-a767-3298a73703cb" + }, + "incoming_channel_id": { + "type": "string", + "example": "1095780203256627291" + }, + "name": { + "type": "string", + "example": "Game Server" + }, + "server_id": { + "type": "string", + "example": "1095778291488653372" + }, + "updated_at": { + "type": "string", + "example": "2022-06-05T14:26:10.303278+03:00" + }, + "user_id": { + "type": "string", + "example": "WB7DRDWrJZRGbYrv2CKGkqbzvqdC" + } + } + }, + "entities.Heartbeat": { + "type": "object", + "required": [ + "charging", + "id", + "owner", + "timestamp", + "user_id", + "version" + ], + "properties": { + "charging": { + "type": "boolean", + "example": true + }, + "id": { + "type": "string", + "example": "32343a19-da5e-4b1b-a767-3298a73703cb" + }, + "owner": { + "type": "string", + "example": "+18005550199" + }, + "timestamp": { + "type": "string", + "example": "2022-06-05T14:26:01.520828+03:00" + }, + "user_id": { + "type": "string", + "example": "WB7DRDWrJZRGbYrv2CKGkqbzvqdC" + }, + "version": { + "type": "string", + "example": "344c10f" + } + } + }, + "entities.Message": { + "type": "object", + "required": [ + "attachments", + "contact", + "content", + "created_at", + "encrypted", + "id", + "max_send_attempts", + "order_timestamp", + "owner", + "request_received_at", + "send_attempt_count", + "sim", + "status", + "type", + "updated_at", + "user_id" + ], + "properties": { + "attachments": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "https://example.com/image.jpg", + "https://example.com/video.mp4" + ] + }, + "contact": { + "type": "string", + "example": "+18005550100" + }, + "content": { + "type": "string", + "example": "This is a sample text message" + }, + "created_at": { + "type": "string", + "example": "2022-06-05T14:26:02.302718+03:00" + }, + "delivered_at": { + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "encrypted": { + "type": "boolean", + "example": false + }, + "expired_at": { + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "failed_at": { + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "failure_reason": { + "type": "string", + "example": "UNKNOWN" + }, + "id": { + "type": "string", + "example": "32343a19-da5e-4b1b-a767-3298a73703cb" + }, + "last_attempted_at": { + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "max_send_attempts": { + "type": "integer", + "example": 1 + }, + "order_timestamp": { + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "owner": { + "type": "string", + "example": "+18005550199" + }, + "received_at": { + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "request_id": { + "type": "string", + "example": "153554b5-ae44-44a0-8f4f-7bbac5657ad4" + }, + "request_received_at": { + "type": "string", + "example": "2022-06-05T14:26:01.520828+03:00" + }, + "scheduled_at": { + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "scheduled_send_time": { + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "send_attempt_count": { + "type": "integer", + "example": 0 + }, + "send_time": { + "description": "SendDuration is the number of nanoseconds from when the request was received until when the mobile phone send the message", + "type": "integer", + "example": 133414 + }, + "sent_at": { + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "sim": { + "description": "SIM is the SIM card to use to send the message\n* SMS1: use the SIM card in slot 1\n* SMS2: use the SIM card in slot 2\n* DEFAULT: used the default communication SIM card", + "allOf": [ + { + "$ref": "#/definitions/entities.SIM" + } + ], + "example": "DEFAULT" + }, + "status": { + "type": "string", + "example": "pending" + }, + "type": { + "type": "string", + "example": "mobile-terminated" + }, + "updated_at": { + "type": "string", + "example": "2022-06-05T14:26:10.303278+03:00" + }, + "user_id": { + "type": "string", + "example": "WB7DRDWrJZRGbYrv2CKGkqbzvqdC" + } + } + }, + "entities.MessageThread": { + "type": "object", + "required": [ + "color", + "contact", + "created_at", + "id", + "is_archived", + "last_message_content", + "last_message_id", + "order_timestamp", + "owner", + "status", + "updated_at", + "user_id" + ], + "properties": { + "color": { + "type": "string", + "example": "indigo" + }, + "contact": { + "type": "string", + "example": "+18005550100" + }, + "created_at": { + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "id": { + "type": "string", + "example": "32343a19-da5e-4b1b-a767-3298a73703ca" + }, + "is_archived": { + "type": "boolean", + "example": false + }, + "last_message_content": { + "type": "string", + "example": "This is a sample message content" + }, + "last_message_id": { + "type": "string", + "example": "32343a19-da5e-4b1b-a767-3298a73703ca" + }, + "order_timestamp": { + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "owner": { + "type": "string", + "example": "+18005550199" + }, + "status": { + "type": "string", + "example": "PENDING" + }, + "updated_at": { + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "user_id": { + "type": "string", + "example": "WB7DRDWrJZRGbYrv2CKGkqbzvqdC" + } + } + }, + "entities.Phone": { + "type": "object", + "required": [ + "created_at", + "id", + "max_send_attempts", + "message_expiration_seconds", + "messages_per_minute", + "phone_number", + "sim", + "updated_at", + "user_id" + ], + "properties": { + "created_at": { + "type": "string", + "example": "2022-06-05T14:26:02.302718+03:00" + }, + "fcm_token": { + "type": "string", + "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzd....." + }, + "id": { + "type": "string", + "example": "32343a19-da5e-4b1b-a767-3298a73703cb" + }, + "max_send_attempts": { + "description": "MaxSendAttempts determines how many times to retry sending an SMS message", + "type": "integer", + "example": 2 + }, + "message_expiration_seconds": { + "description": "MessageExpirationSeconds is the duration in seconds after sending a message when it is considered to be expired.", + "type": "integer" + }, + "messages_per_minute": { + "type": "integer", + "example": 1 + }, + "missed_call_auto_reply": { + "type": "string", + "example": "This phone cannot receive calls. Please send an SMS instead." + }, + "phone_number": { + "type": "string", + "example": "+18005550199" + }, + "sim": { + "$ref": "#/definitions/entities.SIM" + }, + "updated_at": { + "type": "string", + "example": "2022-06-05T14:26:10.303278+03:00" + }, + "user_id": { + "type": "string", + "example": "WB7DRDWrJZRGbYrv2CKGkqbzvqdC" + } + } + }, + "entities.PhoneAPIKey": { + "type": "object", + "required": [ + "api_key", + "created_at", + "id", + "name", + "phone_ids", + "phone_numbers", + "updated_at", + "user_email", + "user_id" + ], + "properties": { + "api_key": { + "type": "string", + "example": "pk_DGW8NwQp7mxKaSZ72Xq9v6xxxxx" + }, + "created_at": { + "type": "string", + "example": "2022-06-05T14:26:02.302718+03:00" + }, + "id": { + "type": "string", + "example": "32343a19-da5e-4b1b-a767-3298a73703cb" + }, + "name": { + "type": "string", + "example": "Business Phone Key" + }, + "phone_ids": { + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "32343a19-da5e-4b1b-a767-3298a73703cb", + "32343a19-da5e-4b1b-a767-3298a73703cc" + ] + }, + "phone_numbers": { + "type": "array", + "items": { + "type": "string" + }, + "example": ["+18005550199", "+18005550100"] + }, + "updated_at": { + "type": "string", + "example": "2022-06-05T14:26:02.302718+03:00" + }, + "user_email": { + "type": "string", + "example": "user@gmail.com" + }, + "user_id": { + "type": "string", + "example": "WB7DRDWrJZRGbYrv2CKGkqbzvqdC" + } + } + }, + "entities.SIM": { + "type": "string", + "enum": ["SIM1", "SIM2"], + "x-enum-varnames": ["SIM1", "SIM2"] + }, + "entities.SubscriptionName": { + "type": "string", + "enum": [ + "free", + "pro-monthly", + "pro-yearly", + "ultra-monthly", + "ultra-yearly", + "pro-lifetime", + "20k-monthly", + "100k-monthly", + "50k-monthly", + "200k-monthly", + "20k-yearly" + ], + "x-enum-varnames": [ + "SubscriptionNameFree", + "SubscriptionNameProMonthly", + "SubscriptionNameProYearly", + "SubscriptionNameUltraMonthly", + "SubscriptionNameUltraYearly", + "SubscriptionNameProLifetime", + "SubscriptionName20KMonthly", + "SubscriptionName100KMonthly", + "SubscriptionName50KMonthly", + "SubscriptionName200KMonthly", + "SubscriptionName20KYearly" + ] + }, + "entities.User": { + "type": "object", + "required": [ + "api_key", + "created_at", + "email", + "id", + "notification_heartbeat_enabled", + "notification_message_status_enabled", + "notification_newsletter_enabled", + "notification_webhook_enabled", + "subscription_id", + "subscription_name", + "timezone", + "updated_at" + ], + "properties": { + "active_phone_id": { + "type": "string", + "example": "32343a19-da5e-4b1b-a767-3298a73703cb" + }, + "api_key": { + "type": "string", + "example": "x-api-key" + }, + "created_at": { + "type": "string", + "example": "2022-06-05T14:26:02.302718+03:00" + }, + "email": { + "type": "string", + "example": "name@email.com" + }, + "id": { + "type": "string", + "example": "WB7DRDWrJZRGbYrv2CKGkqbzvqdC" + }, + "notification_heartbeat_enabled": { + "type": "boolean", + "example": true + }, + "notification_message_status_enabled": { + "type": "boolean", + "example": true + }, + "notification_newsletter_enabled": { + "type": "boolean", + "example": true + }, + "notification_webhook_enabled": { + "type": "boolean", + "example": true + }, + "subscription_ends_at": { + "type": "string", + "example": "2022-06-05T14:26:02.302718+03:00" + }, + "subscription_id": { + "type": "string", + "example": "8f9c71b8-b84e-4417-8408-a62274f65a08" + }, + "subscription_name": { + "allOf": [ + { + "$ref": "#/definitions/entities.SubscriptionName" + } + ], + "example": "free" + }, + "subscription_renews_at": { + "type": "string", + "example": "2022-06-05T14:26:02.302718+03:00" + }, + "subscription_status": { + "type": "string", + "example": "on_trial" + }, + "timezone": { + "type": "string", + "example": "Europe/Helsinki" + }, + "updated_at": { + "type": "string", + "example": "2022-06-05T14:26:10.303278+03:00" + } + } + }, + "entities.Webhook": { + "type": "object", + "required": [ + "created_at", + "events", + "id", + "phone_numbers", + "signing_key", + "updated_at", + "url", + "user_id" + ], + "properties": { + "created_at": { + "type": "string", + "example": "2022-06-05T14:26:02.302718+03:00" + }, + "events": { + "type": "array", + "items": { + "type": "string" + }, + "example": ["message.phone.received"] + }, + "id": { + "type": "string", + "example": "32343a19-da5e-4b1b-a767-3298a73703cb" + }, + "phone_numbers": { + "type": "array", + "items": { + "type": "string" + }, + "example": ["+18005550199", "+18005550100"] + }, + "signing_key": { + "type": "string", + "example": "DGW8NwQp7mxKaSZ72Xq9v67SLqSbWQvckzzmK8D6rvd7NywSEkdMJtuxKyEkYnCY" + }, + "updated_at": { + "type": "string", + "example": "2022-06-05T14:26:10.303278+03:00" + }, + "url": { + "type": "string", + "example": "https://example.com" + }, + "user_id": { + "type": "string", + "example": "WB7DRDWrJZRGbYrv2CKGkqbzvqdC" + } + } + }, + "requests.DiscordStore": { + "type": "object", + "required": ["incoming_channel_id", "name", "server_id"], + "properties": { + "incoming_channel_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "server_id": { + "type": "string" + } + } + }, + "requests.DiscordUpdate": { + "type": "object", + "required": ["incoming_channel_id", "name", "server_id"], + "properties": { + "incoming_channel_id": { + "type": "string" + }, + "name": { + "type": "string" + }, + "server_id": { + "type": "string" + } + } + }, + "requests.HeartbeatStore": { + "type": "object", + "required": ["charging", "phone_numbers"], + "properties": { + "charging": { + "type": "boolean" + }, + "phone_numbers": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "requests.MessageAttachment": { + "type": "object", + "required": ["content", "content_type", "name"], + "properties": { + "content": { + "description": "Content is the base64-encoded attachment data", + "type": "string", + "example": "base64data..." + }, + "content_type": { + "description": "ContentType is the MIME type of the attachment", + "type": "string", + "example": "image/jpeg" + }, + "name": { + "description": "Name is the original filename of the attachment", + "type": "string", + "example": "photo.jpg" + } + } + }, + "requests.MessageBulkSend": { + "type": "object", + "required": ["content", "from", "to"], + "properties": { + "attachments": { + "description": "Attachments are optional. When you provide a list of attachments, the message will be sent out as an MMS", + "type": "array", + "items": { + "type": "string" + } + }, + "content": { + "type": "string", + "example": "This is a sample text message" + }, + "encrypted": { + "description": "Encrypted is used to determine if the content is end-to-end encrypted. Make sure to set the encryption key on the httpSMS mobile app", + "type": "boolean", + "example": false + }, + "from": { + "type": "string", + "example": "+18005550199" + }, + "request_id": { + "description": "RequestID is an optional parameter used to track a request from the client's perspective", + "type": "string", + "example": "153554b5-ae44-44a0-8f4f-7bbac5657ad4" + }, + "to": { + "type": "array", + "items": { + "type": "string" + }, + "example": ["+18005550100", "+18005550100"] + } + } + }, + "requests.MessageCallMissed": { + "type": "object", + "required": ["from", "sim", "timestamp", "to"], + "properties": { + "from": { + "type": "string", + "example": "+18005550199" + }, + "sim": { + "type": "string", + "example": "SIM1" + }, + "timestamp": { + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "to": { + "type": "string", + "example": "+18005550100" + } + } + }, + "requests.MessageEvent": { + "type": "object", + "required": ["event_name", "reason", "timestamp"], + "properties": { + "event_name": { + "description": "EventName is the type of event\n* SENT: is emitted when a message is sent by the mobile phone\n* FAILED: is event is emitted when the message could not be sent by the mobile phone\n* DELIVERED: is event is emitted when a delivery report has been received by the mobile phone", + "type": "string", + "example": "SENT" + }, + "reason": { + "description": "Reason is the exact error message in case the event is an error", + "type": "string" + }, + "timestamp": { + "description": "Timestamp is the time when the event was emitted, Please send the timestamp in UTC with as much precision as possible", + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + } + } + }, + "requests.MessageReceive": { + "type": "object", + "required": ["content", "encrypted", "from", "sim", "timestamp", "to"], + "properties": { + "attachments": { + "description": "Attachments is the list of MMS attachments received with the message", + "type": "array", + "items": { + "$ref": "#/definitions/requests.MessageAttachment" + } + }, + "content": { + "type": "string", + "example": "This is a sample text message received on a phone" + }, + "encrypted": { + "description": "Encrypted is used to determine if the content is end-to-end encrypted. Make sure to set the encryption key on the httpSMS mobile app", + "type": "boolean", + "example": false + }, + "from": { + "type": "string", + "example": "+18005550199" + }, + "sim": { + "description": "SIM card that received the message", + "allOf": [ + { + "$ref": "#/definitions/entities.SIM" + } + ], + "example": "SIM1" + }, + "timestamp": { + "description": "Timestamp is the time when the event was emitted, Please send the timestamp in UTC with as much precision as possible", + "type": "string", + "example": "2022-06-05T14:26:09.527976+03:00" + }, + "to": { + "type": "string", + "example": "+18005550100" + } + } + }, + "requests.MessageSend": { + "type": "object", + "required": ["content", "from", "to"], + "properties": { + "attachments": { + "description": "Attachments are optional. When you provide a list of attachments, the message will be sent out as an MMS", + "type": "array", + "items": { + "type": "string" + }, + "example": [ + "https://example.com/image.jpg", + "https://example.com/video.mp4" + ] + }, + "content": { + "type": "string", + "example": "This is a sample text message" + }, + "encrypted": { + "description": "Encrypted is an optional parameter used to determine if the content is end-to-end encrypted. Make sure to set the encryption key on the httpSMS mobile app", + "type": "boolean", + "example": false + }, + "from": { + "type": "string", + "example": "+18005550199" + }, + "request_id": { + "description": "RequestID is an optional parameter used to track a request from the client's perspective", + "type": "string", + "example": "153554b5-ae44-44a0-8f4f-7bbac5657ad4" + }, + "send_at": { + "description": "SendAt is an optional parameter used to schedule a message to be sent in the future. The time is considered to be in your profile's local timezone and you can queue messages for up to 20 days (480 hours) in the future.", + "type": "string", + "example": "2025-12-19T16:39:57-08:00" + }, + "to": { + "type": "string", + "example": "+18005550100" + } + } + }, + "requests.MessageThreadUpdate": { + "type": "object", + "required": ["is_archived"], + "properties": { + "is_archived": { + "type": "boolean", + "example": true + } + } + }, + "requests.PhoneAPIKeyStoreRequest": { + "type": "object", + "required": ["name"], + "properties": { + "name": { + "type": "string", + "example": "My Phone API Key" + } + } + }, + "requests.PhoneFCMToken": { + "type": "object", + "required": ["fcm_token", "phone_number", "sim"], + "properties": { + "fcm_token": { + "type": "string", + "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzd....." + }, + "phone_number": { + "type": "string", + "example": "[+18005550199]" + }, + "sim": { + "description": "SIM is the SIM slot of the phone in case the phone has more than 1 SIM slot", + "type": "string", + "example": "SIM1" + } + } + }, + "requests.PhoneUpsert": { + "type": "object", + "required": [ + "fcm_token", + "max_send_attempts", + "message_expiration_seconds", + "messages_per_minute", + "missed_call_auto_reply", + "phone_number", + "sim" + ], + "properties": { + "fcm_token": { + "type": "string", + "example": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzd....." + }, + "max_send_attempts": { + "description": "MaxSendAttempts is the number of attempts when sending an SMS message to handle the case where the phone is offline.", + "type": "integer", + "example": 2 + }, + "message_expiration_seconds": { + "description": "MessageExpirationSeconds is the duration in seconds after sending a message when it is considered to be expired.", + "type": "integer", + "example": 12345 + }, + "messages_per_minute": { + "type": "integer", + "example": 1 + }, + "missed_call_auto_reply": { + "type": "string", + "example": "e.g. This phone cannot receive calls. Please send an SMS instead." + }, + "phone_number": { + "type": "string", + "example": "+18005550199" + }, + "sim": { + "description": "SIM is the SIM slot of the phone in case the phone has more than 1 SIM slot", + "type": "string", + "example": "SIM1" + } + } + }, + "requests.UserNotificationUpdate": { + "type": "object", + "required": [ + "heartbeat_enabled", + "message_status_enabled", + "newsletter_enabled", + "webhook_enabled" + ], + "properties": { + "heartbeat_enabled": { + "type": "boolean", + "example": true + }, + "message_status_enabled": { + "type": "boolean", + "example": true + }, + "newsletter_enabled": { + "type": "boolean", + "example": true + }, + "webhook_enabled": { + "type": "boolean", + "example": true + } + } + }, + "requests.UserPaymentInvoice": { + "type": "object", + "required": [ + "address", + "city", + "country", + "name", + "notes", + "state", + "zip_code" + ], + "properties": { + "address": { + "type": "string", + "example": "221B Baker Street, London" + }, + "city": { + "type": "string", + "example": "Los Angeles" + }, + "country": { + "type": "string", + "example": "US" + }, + "name": { + "type": "string", + "example": "Acme Corp" + }, + "notes": { + "type": "string", + "example": "Thank you for your business!" + }, + "state": { + "type": "string", + "example": "CA" + }, + "zip_code": { + "type": "string", + "example": "9800" + } + } + }, + "requests.UserUpdate": { + "type": "object", + "required": ["active_phone_id", "timezone"], + "properties": { + "active_phone_id": { + "type": "string", + "example": "32343a19-da5e-4b1b-a767-3298a73703cb" + }, + "timezone": { + "type": "string", + "example": "Europe/Helsinki" + } + } + }, + "requests.WebhookStore": { + "type": "object", + "required": ["events", "phone_numbers", "signing_key", "url"], + "properties": { + "events": { + "type": "array", + "items": { + "type": "string" + } + }, + "phone_numbers": { + "type": "array", + "items": { + "type": "string" + }, + "example": ["+18005550100", "+18005550100"] + }, + "signing_key": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "requests.WebhookUpdate": { + "type": "object", + "required": ["events", "phone_numbers", "signing_key", "url"], + "properties": { + "events": { + "type": "array", + "items": { + "type": "string" + } + }, + "phone_numbers": { + "type": "array", + "items": { + "type": "string" + }, + "example": ["+18005550100", "+18005550100"] + }, + "signing_key": { + "type": "string" + }, + "url": { + "type": "string" + } + } + }, + "responses.BadRequest": { + "type": "object", + "required": ["data", "message", "status"], + "properties": { + "data": { + "type": "string", + "example": "The request body is not a valid JSON string" + }, + "message": { + "type": "string", + "example": "The request isn't properly formed" + }, + "status": { + "type": "string", + "example": "error" + } + } + }, + "responses.BillingUsageResponse": { + "type": "object", + "required": ["data", "message", "status"], + "properties": { + "data": { + "$ref": "#/definitions/entities.BillingUsage" + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.BillingUsagesResponse": { + "type": "object", + "required": ["data", "message", "status"], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/entities.BillingUsage" + } + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.DiscordResponse": { + "type": "object", + "required": ["data", "message", "status"], + "properties": { + "data": { + "$ref": "#/definitions/entities.Discord" + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.DiscordsResponse": { + "type": "object", + "required": ["data", "message", "status"], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/entities.Discord" + } + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.HeartbeatResponse": { + "type": "object", + "required": ["data", "message", "status"], + "properties": { + "data": { + "$ref": "#/definitions/entities.Heartbeat" + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.HeartbeatsResponse": { + "type": "object", + "required": ["data", "message", "status"], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/entities.Heartbeat" + } + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.InternalServerError": { + "type": "object", + "required": ["message", "status"], + "properties": { + "message": { + "type": "string", + "example": "We ran into an internal error while handling the request." + }, + "status": { + "type": "string", + "example": "error" + } + } + }, + "responses.MessageResponse": { + "type": "object", + "required": ["data", "message", "status"], + "properties": { + "data": { + "$ref": "#/definitions/entities.Message" + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.MessageThreadsResponse": { + "type": "object", + "required": ["data", "message", "status"], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/entities.MessageThread" + } + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.MessagesResponse": { + "type": "object", + "required": ["data", "message", "status"], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/entities.Message" + } + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.NoContent": { + "type": "object", + "required": ["message", "status"], + "properties": { + "message": { + "type": "string", + "example": "action performed successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.NotFound": { + "type": "object", + "required": ["message", "status"], + "properties": { + "message": { + "type": "string", + "example": "cannot find message with ID [32343a19-da5e-4b1b-a767-3298a73703ca]" + }, + "status": { + "type": "string", + "example": "error" + } + } + }, + "responses.OkString": { + "type": "object", + "required": ["data", "message", "status"], + "properties": { + "data": { + "type": "string" + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.PhoneAPIKeyResponse": { + "type": "object", + "required": ["data", "message", "status"], + "properties": { + "data": { + "$ref": "#/definitions/entities.PhoneAPIKey" + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.PhoneAPIKeysResponse": { + "type": "object", + "required": ["data", "message", "status"], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/entities.PhoneAPIKey" + } + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.PhoneResponse": { + "type": "object", + "required": ["data", "message", "status"], + "properties": { + "data": { + "$ref": "#/definitions/entities.Phone" + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.PhonesResponse": { + "type": "object", + "required": ["data", "message", "status"], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/entities.Phone" + } + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.Unauthorized": { + "type": "object", + "required": ["data", "message", "status"], + "properties": { + "data": { + "type": "string", + "example": "Make sure your API key is set in the [X-API-Key] header in the request" + }, + "message": { + "type": "string", + "example": "You are not authorized to carry out this request." + }, + "status": { + "type": "string", + "example": "error" + } + } + }, + "responses.UnprocessableEntity": { + "type": "object", + "required": ["data", "message", "status"], + "properties": { + "data": { + "type": "object", + "additionalProperties": { + "type": "array", + "items": { + "type": "string" + } + } + }, + "message": { + "type": "string", + "example": "validation errors while handling request" + }, + "status": { + "type": "string", + "example": "error" + } + } + }, + "responses.UserResponse": { + "type": "object", + "required": ["data", "message", "status"], + "properties": { + "data": { + "$ref": "#/definitions/entities.User" + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.UserSubscriptionPaymentsResponse": { + "type": "object", + "required": ["data", "message", "status"], + "properties": { + "data": { + "type": "array", + "items": { + "type": "object", + "required": ["attributes", "id", "type"], + "properties": { + "attributes": { + "type": "object", + "required": [ + "billing_reason", + "card_brand", + "card_last_four", + "created_at", + "currency", + "currency_rate", + "discount_total", + "discount_total_formatted", + "discount_total_usd", + "refunded", + "refunded_amount", + "refunded_amount_formatted", + "refunded_amount_usd", + "refunded_at", + "status", + "status_formatted", + "subtotal", + "subtotal_formatted", + "subtotal_usd", + "tax", + "tax_formatted", + "tax_inclusive", + "tax_usd", + "total", + "total_formatted", + "total_usd", + "updated_at" + ], + "properties": { + "billing_reason": { + "type": "string" + }, + "card_brand": { + "type": "string" + }, + "card_last_four": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "currency": { + "type": "string" + }, + "currency_rate": { + "type": "string" + }, + "discount_total": { + "type": "integer" + }, + "discount_total_formatted": { + "type": "string" + }, + "discount_total_usd": { + "type": "integer" + }, + "refunded": { + "type": "boolean" + }, + "refunded_amount": { + "type": "integer" + }, + "refunded_amount_formatted": { + "type": "string" + }, + "refunded_amount_usd": { + "type": "integer" + }, + "refunded_at": {}, + "status": { + "type": "string" + }, + "status_formatted": { + "type": "string" + }, + "subtotal": { + "type": "integer" + }, + "subtotal_formatted": { + "type": "string" + }, + "subtotal_usd": { + "type": "integer" + }, + "tax": { + "type": "integer" + }, + "tax_formatted": { + "type": "string" + }, + "tax_inclusive": { + "type": "boolean" + }, + "tax_usd": { + "type": "integer" + }, + "total": { + "type": "integer" + }, + "total_formatted": { + "type": "string" + }, + "total_usd": { + "type": "integer" + }, + "updated_at": { + "type": "string" + } + } + }, + "id": { + "type": "string" + }, + "type": { + "type": "string" + } + } + } + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.WebhookResponse": { + "type": "object", + "required": ["data", "message", "status"], + "properties": { + "data": { + "$ref": "#/definitions/entities.Webhook" + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + }, + "responses.WebhooksResponse": { + "type": "object", + "required": ["data", "message", "status"], + "properties": { + "data": { + "type": "array", + "items": { + "$ref": "#/definitions/entities.Webhook" + } + }, + "message": { + "type": "string", + "example": "Request handled successfully" + }, + "status": { + "type": "string", + "example": "success" + } + } + } + }, + "securityDefinitions": { + "ApiKeyAuth": { + "type": "apiKey", + "name": "x-api-Key", + "in": "header" + } + } +} diff --git a/api/docs/swagger.yaml b/api/docs/swagger.yaml new file mode 100644 index 00000000..f5563f2a --- /dev/null +++ b/api/docs/swagger.yaml @@ -0,0 +1,3396 @@ +basePath: /v1 +definitions: + entities.BillingUsage: + properties: + created_at: + example: "2022-06-05T14:26:02.302718+03:00" + type: string + end_timestamp: + example: "2022-01-31T23:59:59+00:00" + type: string + id: + example: 32343a19-da5e-4b1b-a767-3298a73703cb + type: string + received_messages: + example: 465 + type: integer + sent_messages: + example: 321 + type: integer + start_timestamp: + example: "2022-01-01T00:00:00+00:00" + type: string + total_cost: + example: 0 + type: integer + updated_at: + example: "2022-06-05T14:26:10.303278+03:00" + type: string + user_id: + example: WB7DRDWrJZRGbYrv2CKGkqbzvqdC + type: string + required: + - created_at + - end_timestamp + - id + - received_messages + - sent_messages + - start_timestamp + - total_cost + - updated_at + - user_id + type: object + entities.Discord: + properties: + created_at: + example: "2022-06-05T14:26:02.302718+03:00" + type: string + id: + example: 32343a19-da5e-4b1b-a767-3298a73703cb + type: string + incoming_channel_id: + example: "1095780203256627291" + type: string + name: + example: Game Server + type: string + server_id: + example: "1095778291488653372" + type: string + updated_at: + example: "2022-06-05T14:26:10.303278+03:00" + type: string + user_id: + example: WB7DRDWrJZRGbYrv2CKGkqbzvqdC + type: string + required: + - created_at + - id + - incoming_channel_id + - name + - server_id + - updated_at + - user_id + type: object + entities.Heartbeat: + properties: + charging: + example: true + type: boolean + id: + example: 32343a19-da5e-4b1b-a767-3298a73703cb + type: string + owner: + example: "+18005550199" + type: string + timestamp: + example: "2022-06-05T14:26:01.520828+03:00" + type: string + user_id: + example: WB7DRDWrJZRGbYrv2CKGkqbzvqdC + type: string + version: + example: 344c10f + type: string + required: + - charging + - id + - owner + - timestamp + - user_id + - version + type: object + entities.Message: + properties: + attachments: + example: + - https://example.com/image.jpg + - https://example.com/video.mp4 + items: + type: string + type: array + contact: + example: "+18005550100" + type: string + content: + example: This is a sample text message + type: string + created_at: + example: "2022-06-05T14:26:02.302718+03:00" + type: string + delivered_at: + example: "2022-06-05T14:26:09.527976+03:00" + type: string + encrypted: + example: false + type: boolean + expired_at: + example: "2022-06-05T14:26:09.527976+03:00" + type: string + failed_at: + example: "2022-06-05T14:26:09.527976+03:00" + type: string + failure_reason: + example: UNKNOWN + type: string + id: + example: 32343a19-da5e-4b1b-a767-3298a73703cb + type: string + last_attempted_at: + example: "2022-06-05T14:26:09.527976+03:00" + type: string + max_send_attempts: + example: 1 + type: integer + order_timestamp: + example: "2022-06-05T14:26:09.527976+03:00" + type: string + owner: + example: "+18005550199" + type: string + received_at: + example: "2022-06-05T14:26:09.527976+03:00" + type: string + request_id: + example: 153554b5-ae44-44a0-8f4f-7bbac5657ad4 + type: string + request_received_at: + example: "2022-06-05T14:26:01.520828+03:00" + type: string + scheduled_at: + example: "2022-06-05T14:26:09.527976+03:00" + type: string + scheduled_send_time: + example: "2022-06-05T14:26:09.527976+03:00" + type: string + send_attempt_count: + example: 0 + type: integer + send_time: + description: + SendDuration is the number of nanoseconds from when the request + was received until when the mobile phone send the message + example: 133414 + type: integer + sent_at: + example: "2022-06-05T14:26:09.527976+03:00" + type: string + sim: + allOf: + - $ref: "#/definitions/entities.SIM" + description: |- + SIM is the SIM card to use to send the message + * SMS1: use the SIM card in slot 1 + * SMS2: use the SIM card in slot 2 + * DEFAULT: used the default communication SIM card + example: DEFAULT + status: + example: pending + type: string + type: + example: mobile-terminated + type: string + updated_at: + example: "2022-06-05T14:26:10.303278+03:00" + type: string + user_id: + example: WB7DRDWrJZRGbYrv2CKGkqbzvqdC + type: string + required: + - attachments + - contact + - content + - created_at + - encrypted + - id + - max_send_attempts + - order_timestamp + - owner + - request_received_at + - send_attempt_count + - sim + - status + - type + - updated_at + - user_id + type: object + entities.MessageThread: + properties: + color: + example: indigo + type: string + contact: + example: "+18005550100" + type: string + created_at: + example: "2022-06-05T14:26:09.527976+03:00" + type: string + id: + example: 32343a19-da5e-4b1b-a767-3298a73703ca + type: string + is_archived: + example: false + type: boolean + last_message_content: + example: This is a sample message content + type: string + last_message_id: + example: 32343a19-da5e-4b1b-a767-3298a73703ca + type: string + order_timestamp: + example: "2022-06-05T14:26:09.527976+03:00" + type: string + owner: + example: "+18005550199" + type: string + status: + example: PENDING + type: string + updated_at: + example: "2022-06-05T14:26:09.527976+03:00" + type: string + user_id: + example: WB7DRDWrJZRGbYrv2CKGkqbzvqdC + type: string + required: + - color + - contact + - created_at + - id + - is_archived + - last_message_content + - last_message_id + - order_timestamp + - owner + - status + - updated_at + - user_id + type: object + entities.Phone: + properties: + created_at: + example: "2022-06-05T14:26:02.302718+03:00" + type: string + fcm_token: + example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzd..... + type: string + id: + example: 32343a19-da5e-4b1b-a767-3298a73703cb + type: string + max_send_attempts: + description: + MaxSendAttempts determines how many times to retry sending an + SMS message + example: 2 + type: integer + message_expiration_seconds: + description: + MessageExpirationSeconds is the duration in seconds after sending + a message when it is considered to be expired. + type: integer + messages_per_minute: + example: 1 + type: integer + missed_call_auto_reply: + example: This phone cannot receive calls. Please send an SMS instead. + type: string + phone_number: + example: "+18005550199" + type: string + sim: + $ref: "#/definitions/entities.SIM" + updated_at: + example: "2022-06-05T14:26:10.303278+03:00" + type: string + user_id: + example: WB7DRDWrJZRGbYrv2CKGkqbzvqdC + type: string + required: + - created_at + - id + - max_send_attempts + - message_expiration_seconds + - messages_per_minute + - phone_number + - sim + - updated_at + - user_id + type: object + entities.PhoneAPIKey: + properties: + api_key: + example: pk_DGW8NwQp7mxKaSZ72Xq9v6xxxxx + type: string + created_at: + example: "2022-06-05T14:26:02.302718+03:00" + type: string + id: + example: 32343a19-da5e-4b1b-a767-3298a73703cb + type: string + name: + example: Business Phone Key + type: string + phone_ids: + example: + - 32343a19-da5e-4b1b-a767-3298a73703cb + - 32343a19-da5e-4b1b-a767-3298a73703cc + items: + type: string + type: array + phone_numbers: + example: + - "+18005550199" + - "+18005550100" + items: + type: string + type: array + updated_at: + example: "2022-06-05T14:26:02.302718+03:00" + type: string + user_email: + example: user@gmail.com + type: string + user_id: + example: WB7DRDWrJZRGbYrv2CKGkqbzvqdC + type: string + required: + - api_key + - created_at + - id + - name + - phone_ids + - phone_numbers + - updated_at + - user_email + - user_id + type: object + entities.SIM: + enum: + - SIM1 + - SIM2 + type: string + x-enum-varnames: + - SIM1 + - SIM2 + entities.SubscriptionName: + enum: + - free + - pro-monthly + - pro-yearly + - ultra-monthly + - ultra-yearly + - pro-lifetime + - 20k-monthly + - 100k-monthly + - 50k-monthly + - 200k-monthly + - 20k-yearly + type: string + x-enum-varnames: + - SubscriptionNameFree + - SubscriptionNameProMonthly + - SubscriptionNameProYearly + - SubscriptionNameUltraMonthly + - SubscriptionNameUltraYearly + - SubscriptionNameProLifetime + - SubscriptionName20KMonthly + - SubscriptionName100KMonthly + - SubscriptionName50KMonthly + - SubscriptionName200KMonthly + - SubscriptionName20KYearly + entities.User: + properties: + active_phone_id: + example: 32343a19-da5e-4b1b-a767-3298a73703cb + type: string + api_key: + example: x-api-key + type: string + created_at: + example: "2022-06-05T14:26:02.302718+03:00" + type: string + email: + example: name@email.com + type: string + id: + example: WB7DRDWrJZRGbYrv2CKGkqbzvqdC + type: string + notification_heartbeat_enabled: + example: true + type: boolean + notification_message_status_enabled: + example: true + type: boolean + notification_newsletter_enabled: + example: true + type: boolean + notification_webhook_enabled: + example: true + type: boolean + subscription_ends_at: + example: "2022-06-05T14:26:02.302718+03:00" + type: string + subscription_id: + example: 8f9c71b8-b84e-4417-8408-a62274f65a08 + type: string + subscription_name: + allOf: + - $ref: "#/definitions/entities.SubscriptionName" + example: free + subscription_renews_at: + example: "2022-06-05T14:26:02.302718+03:00" + type: string + subscription_status: + example: on_trial + type: string + timezone: + example: Europe/Helsinki + type: string + updated_at: + example: "2022-06-05T14:26:10.303278+03:00" + type: string + required: + - api_key + - created_at + - email + - id + - notification_heartbeat_enabled + - notification_message_status_enabled + - notification_newsletter_enabled + - notification_webhook_enabled + - subscription_id + - subscription_name + - timezone + - updated_at + type: object + entities.Webhook: + properties: + created_at: + example: "2022-06-05T14:26:02.302718+03:00" + type: string + events: + example: + - message.phone.received + items: + type: string + type: array + id: + example: 32343a19-da5e-4b1b-a767-3298a73703cb + type: string + phone_numbers: + example: + - "+18005550199" + - "+18005550100" + items: + type: string + type: array + signing_key: + example: DGW8NwQp7mxKaSZ72Xq9v67SLqSbWQvckzzmK8D6rvd7NywSEkdMJtuxKyEkYnCY + type: string + updated_at: + example: "2022-06-05T14:26:10.303278+03:00" + type: string + url: + example: https://example.com + type: string + user_id: + example: WB7DRDWrJZRGbYrv2CKGkqbzvqdC + type: string + required: + - created_at + - events + - id + - phone_numbers + - signing_key + - updated_at + - url + - user_id + type: object + requests.DiscordStore: + properties: + incoming_channel_id: + type: string + name: + type: string + server_id: + type: string + required: + - incoming_channel_id + - name + - server_id + type: object + requests.DiscordUpdate: + properties: + incoming_channel_id: + type: string + name: + type: string + server_id: + type: string + required: + - incoming_channel_id + - name + - server_id + type: object + requests.HeartbeatStore: + properties: + charging: + type: boolean + phone_numbers: + items: + type: string + type: array + required: + - charging + - phone_numbers + type: object + requests.MessageAttachment: + properties: + content: + description: Content is the base64-encoded attachment data + example: base64data... + type: string + content_type: + description: ContentType is the MIME type of the attachment + example: image/jpeg + type: string + name: + description: Name is the original filename of the attachment + example: photo.jpg + type: string + required: + - content + - content_type + - name + type: object + requests.MessageBulkSend: + properties: + attachments: + description: + Attachments are optional. When you provide a list of attachments, + the message will be sent out as an MMS + items: + type: string + type: array + content: + example: This is a sample text message + type: string + encrypted: + description: + Encrypted is used to determine if the content is end-to-end encrypted. + Make sure to set the encryption key on the httpSMS mobile app + example: false + type: boolean + from: + example: "+18005550199" + type: string + request_id: + description: + RequestID is an optional parameter used to track a request from + the client's perspective + example: 153554b5-ae44-44a0-8f4f-7bbac5657ad4 + type: string + to: + example: + - "+18005550100" + - "+18005550100" + items: + type: string + type: array + required: + - content + - from + - to + type: object + requests.MessageCallMissed: + properties: + from: + example: "+18005550199" + type: string + sim: + example: SIM1 + type: string + timestamp: + example: "2022-06-05T14:26:09.527976+03:00" + type: string + to: + example: "+18005550100" + type: string + required: + - from + - sim + - timestamp + - to + type: object + requests.MessageEvent: + properties: + event_name: + description: |- + EventName is the type of event + * SENT: is emitted when a message is sent by the mobile phone + * FAILED: is event is emitted when the message could not be sent by the mobile phone + * DELIVERED: is event is emitted when a delivery report has been received by the mobile phone + example: SENT + type: string + reason: + description: Reason is the exact error message in case the event is an error + type: string + timestamp: + description: + Timestamp is the time when the event was emitted, Please send + the timestamp in UTC with as much precision as possible + example: "2022-06-05T14:26:09.527976+03:00" + type: string + required: + - event_name + - reason + - timestamp + type: object + requests.MessageReceive: + properties: + attachments: + description: + Attachments is the list of MMS attachments received with the + message + items: + $ref: "#/definitions/requests.MessageAttachment" + type: array + content: + example: This is a sample text message received on a phone + type: string + encrypted: + description: + Encrypted is used to determine if the content is end-to-end encrypted. + Make sure to set the encryption key on the httpSMS mobile app + example: false + type: boolean + from: + example: "+18005550199" + type: string + sim: + allOf: + - $ref: "#/definitions/entities.SIM" + description: SIM card that received the message + example: SIM1 + timestamp: + description: + Timestamp is the time when the event was emitted, Please send + the timestamp in UTC with as much precision as possible + example: "2022-06-05T14:26:09.527976+03:00" + type: string + to: + example: "+18005550100" + type: string + required: + - content + - encrypted + - from + - sim + - timestamp + - to + type: object + requests.MessageSend: + properties: + attachments: + description: + Attachments are optional. When you provide a list of attachments, + the message will be sent out as an MMS + example: + - https://example.com/image.jpg + - https://example.com/video.mp4 + items: + type: string + type: array + content: + example: This is a sample text message + type: string + encrypted: + description: + Encrypted is an optional parameter used to determine if the content + is end-to-end encrypted. Make sure to set the encryption key on the httpSMS + mobile app + example: false + type: boolean + from: + example: "+18005550199" + type: string + request_id: + description: + RequestID is an optional parameter used to track a request from + the client's perspective + example: 153554b5-ae44-44a0-8f4f-7bbac5657ad4 + type: string + send_at: + description: + SendAt is an optional parameter used to schedule a message to + be sent in the future. The time is considered to be in your profile's local + timezone and you can queue messages for up to 20 days (480 hours) in the + future. + example: "2025-12-19T16:39:57-08:00" + type: string + to: + example: "+18005550100" + type: string + required: + - content + - from + - to + type: object + requests.MessageThreadUpdate: + properties: + is_archived: + example: true + type: boolean + required: + - is_archived + type: object + requests.PhoneAPIKeyStoreRequest: + properties: + name: + example: My Phone API Key + type: string + required: + - name + type: object + requests.PhoneFCMToken: + properties: + fcm_token: + example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzd..... + type: string + phone_number: + example: "[+18005550199]" + type: string + sim: + description: + SIM is the SIM slot of the phone in case the phone has more than + 1 SIM slot + example: SIM1 + type: string + required: + - fcm_token + - phone_number + - sim + type: object + requests.PhoneUpsert: + properties: + fcm_token: + example: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzd..... + type: string + max_send_attempts: + description: + MaxSendAttempts is the number of attempts when sending an SMS + message to handle the case where the phone is offline. + example: 2 + type: integer + message_expiration_seconds: + description: + MessageExpirationSeconds is the duration in seconds after sending + a message when it is considered to be expired. + example: 12345 + type: integer + messages_per_minute: + example: 1 + type: integer + missed_call_auto_reply: + example: e.g. This phone cannot receive calls. Please send an SMS instead. + type: string + phone_number: + example: "+18005550199" + type: string + sim: + description: + SIM is the SIM slot of the phone in case the phone has more than + 1 SIM slot + example: SIM1 + type: string + required: + - fcm_token + - max_send_attempts + - message_expiration_seconds + - messages_per_minute + - missed_call_auto_reply + - phone_number + - sim + type: object + requests.UserNotificationUpdate: + properties: + heartbeat_enabled: + example: true + type: boolean + message_status_enabled: + example: true + type: boolean + newsletter_enabled: + example: true + type: boolean + webhook_enabled: + example: true + type: boolean + required: + - heartbeat_enabled + - message_status_enabled + - newsletter_enabled + - webhook_enabled + type: object + requests.UserPaymentInvoice: + properties: + address: + example: 221B Baker Street, London + type: string + city: + example: Los Angeles + type: string + country: + example: US + type: string + name: + example: Acme Corp + type: string + notes: + example: Thank you for your business! + type: string + state: + example: CA + type: string + zip_code: + example: "9800" + type: string + required: + - address + - city + - country + - name + - notes + - state + - zip_code + type: object + requests.UserUpdate: + properties: + active_phone_id: + example: 32343a19-da5e-4b1b-a767-3298a73703cb + type: string + timezone: + example: Europe/Helsinki + type: string + required: + - active_phone_id + - timezone + type: object + requests.WebhookStore: + properties: + events: + items: + type: string + type: array + phone_numbers: + example: + - "+18005550100" + - "+18005550100" + items: + type: string + type: array + signing_key: + type: string + url: + type: string + required: + - events + - phone_numbers + - signing_key + - url + type: object + requests.WebhookUpdate: + properties: + events: + items: + type: string + type: array + phone_numbers: + example: + - "+18005550100" + - "+18005550100" + items: + type: string + type: array + signing_key: + type: string + url: + type: string + required: + - events + - phone_numbers + - signing_key + - url + type: object + responses.BadRequest: + properties: + data: + example: The request body is not a valid JSON string + type: string + message: + example: The request isn't properly formed + type: string + status: + example: error + type: string + required: + - data + - message + - status + type: object + responses.BillingUsageResponse: + properties: + data: + $ref: "#/definitions/entities.BillingUsage" + message: + example: Request handled successfully + type: string + status: + example: success + type: string + required: + - data + - message + - status + type: object + responses.BillingUsagesResponse: + properties: + data: + items: + $ref: "#/definitions/entities.BillingUsage" + type: array + message: + example: Request handled successfully + type: string + status: + example: success + type: string + required: + - data + - message + - status + type: object + responses.DiscordResponse: + properties: + data: + $ref: "#/definitions/entities.Discord" + message: + example: Request handled successfully + type: string + status: + example: success + type: string + required: + - data + - message + - status + type: object + responses.DiscordsResponse: + properties: + data: + items: + $ref: "#/definitions/entities.Discord" + type: array + message: + example: Request handled successfully + type: string + status: + example: success + type: string + required: + - data + - message + - status + type: object + responses.HeartbeatResponse: + properties: + data: + $ref: "#/definitions/entities.Heartbeat" + message: + example: Request handled successfully + type: string + status: + example: success + type: string + required: + - data + - message + - status + type: object + responses.HeartbeatsResponse: + properties: + data: + items: + $ref: "#/definitions/entities.Heartbeat" + type: array + message: + example: Request handled successfully + type: string + status: + example: success + type: string + required: + - data + - message + - status + type: object + responses.InternalServerError: + properties: + message: + example: We ran into an internal error while handling the request. + type: string + status: + example: error + type: string + required: + - message + - status + type: object + responses.MessageResponse: + properties: + data: + $ref: "#/definitions/entities.Message" + message: + example: Request handled successfully + type: string + status: + example: success + type: string + required: + - data + - message + - status + type: object + responses.MessageThreadsResponse: + properties: + data: + items: + $ref: "#/definitions/entities.MessageThread" + type: array + message: + example: Request handled successfully + type: string + status: + example: success + type: string + required: + - data + - message + - status + type: object + responses.MessagesResponse: + properties: + data: + items: + $ref: "#/definitions/entities.Message" + type: array + message: + example: Request handled successfully + type: string + status: + example: success + type: string + required: + - data + - message + - status + type: object + responses.NoContent: + properties: + message: + example: action performed successfully + type: string + status: + example: success + type: string + required: + - message + - status + type: object + responses.NotFound: + properties: + message: + example: cannot find message with ID [32343a19-da5e-4b1b-a767-3298a73703ca] + type: string + status: + example: error + type: string + required: + - message + - status + type: object + responses.OkString: + properties: + data: + type: string + message: + example: Request handled successfully + type: string + status: + example: success + type: string + required: + - data + - message + - status + type: object + responses.PhoneAPIKeyResponse: + properties: + data: + $ref: "#/definitions/entities.PhoneAPIKey" + message: + example: Request handled successfully + type: string + status: + example: success + type: string + required: + - data + - message + - status + type: object + responses.PhoneAPIKeysResponse: + properties: + data: + items: + $ref: "#/definitions/entities.PhoneAPIKey" + type: array + message: + example: Request handled successfully + type: string + status: + example: success + type: string + required: + - data + - message + - status + type: object + responses.PhoneResponse: + properties: + data: + $ref: "#/definitions/entities.Phone" + message: + example: Request handled successfully + type: string + status: + example: success + type: string + required: + - data + - message + - status + type: object + responses.PhonesResponse: + properties: + data: + items: + $ref: "#/definitions/entities.Phone" + type: array + message: + example: Request handled successfully + type: string + status: + example: success + type: string + required: + - data + - message + - status + type: object + responses.Unauthorized: + properties: + data: + example: Make sure your API key is set in the [X-API-Key] header in the request + type: string + message: + example: You are not authorized to carry out this request. + type: string + status: + example: error + type: string + required: + - data + - message + - status + type: object + responses.UnprocessableEntity: + properties: + data: + additionalProperties: + items: + type: string + type: array + type: object + message: + example: validation errors while handling request + type: string + status: + example: error + type: string + required: + - data + - message + - status + type: object + responses.UserResponse: + properties: + data: + $ref: "#/definitions/entities.User" + message: + example: Request handled successfully + type: string + status: + example: success + type: string + required: + - data + - message + - status + type: object + responses.UserSubscriptionPaymentsResponse: + properties: + data: + items: + properties: + attributes: + properties: + billing_reason: + type: string + card_brand: + type: string + card_last_four: + type: string + created_at: + type: string + currency: + type: string + currency_rate: + type: string + discount_total: + type: integer + discount_total_formatted: + type: string + discount_total_usd: + type: integer + refunded: + type: boolean + refunded_amount: + type: integer + refunded_amount_formatted: + type: string + refunded_amount_usd: + type: integer + refunded_at: {} + status: + type: string + status_formatted: + type: string + subtotal: + type: integer + subtotal_formatted: + type: string + subtotal_usd: + type: integer + tax: + type: integer + tax_formatted: + type: string + tax_inclusive: + type: boolean + tax_usd: + type: integer + total: + type: integer + total_formatted: + type: string + total_usd: + type: integer + updated_at: + type: string + required: + - billing_reason + - card_brand + - card_last_four + - created_at + - currency + - currency_rate + - discount_total + - discount_total_formatted + - discount_total_usd + - refunded + - refunded_amount + - refunded_amount_formatted + - refunded_amount_usd + - refunded_at + - status + - status_formatted + - subtotal + - subtotal_formatted + - subtotal_usd + - tax + - tax_formatted + - tax_inclusive + - tax_usd + - total + - total_formatted + - total_usd + - updated_at + type: object + id: + type: string + type: + type: string + required: + - attributes + - id + - type + type: object + type: array + message: + example: Request handled successfully + type: string + status: + example: success + type: string + required: + - data + - message + - status + type: object + responses.WebhookResponse: + properties: + data: + $ref: "#/definitions/entities.Webhook" + message: + example: Request handled successfully + type: string + status: + example: success + type: string + required: + - data + - message + - status + type: object + responses.WebhooksResponse: + properties: + data: + items: + $ref: "#/definitions/entities.Webhook" + type: array + message: + example: Request handled successfully + type: string + status: + example: success + type: string + required: + - data + - message + - status + type: object +host: api.httpsms.com +info: + contact: + email: support@httpsms.com + name: support@httpsms.com + description: + Use your Android phone to send and receive SMS messages via a simple + programmable API with end-to-end encryption. + license: + name: AGPL-3.0 + url: https://raw.githubusercontent.com/NdoleStudio/http-sms-manager/main/LICENSE + title: httpSMS API Reference + version: "1.0" +paths: + /billing/usage: + get: + consumes: + - application/json + description: + Get the summary of sent and received messages for a user in the + current month + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.BillingUsageResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Get Billing Usage. + tags: + - Billing + /billing/usage-history: + get: + consumes: + - application/json + description: + Get billing usage records of sent and received messages for a user + in the past. It will be sorted by timestamp in descending order. + parameters: + - description: number of heartbeats to skip + in: query + minimum: 0 + name: skip + type: integer + - description: number of heartbeats to return + in: query + maximum: 100 + minimum: 1 + name: limit + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.BillingUsagesResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Get billing usage history. + tags: + - Billing + /bulk-messages: + post: + consumes: + - multipart/form-data + description: + Sends bulk SMS messages to multiple users based on our [CSV template](https://httpsms.com/templates/httpsms-bulk.csv) + or our [Excel template](https://httpsms.com/templates/httpsms-bulk.xlsx). + parameters: + - description: The Excel or CSV file containing the messages to be sent. + in: formData + name: document + required: true + type: file + produces: + - application/json + responses: + "202": + description: Accepted + schema: + $ref: "#/definitions/responses.NoContent" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Store bulk SMS file + tags: + - BulkSMS + /discord-integrations: + get: + consumes: + - application/json + description: Get the discord integrations of a user + parameters: + - description: number of discord integrations to skip + in: query + minimum: 0 + name: skip + type: integer + - description: filter discord integrations containing query + in: query + name: query + type: string + - description: number of discord integrations to return + in: query + maximum: 20 + minimum: 1 + name: limit + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.DiscordsResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Get discord integrations of a user + tags: + - DiscordIntegration + post: + consumes: + - application/json + description: Store a discord integration for the authenticated user + parameters: + - description: Payload of the discord integration request + in: body + name: payload + required: true + schema: + $ref: "#/definitions/requests.DiscordStore" + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: "#/definitions/responses.DiscordResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Store discord integration + tags: + - DiscordIntegration + /discord-integrations/{discordID}: + delete: + consumes: + - application/json + description: Delete a discord integration for a user + parameters: + - default: 32343a19-da5e-4b1b-a767-3298a73703ca + description: ID of the discord integration + in: path + name: discordID + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + schema: + $ref: "#/definitions/responses.NoContent" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Delete discord integration + tags: + - Webhooks + put: + consumes: + - application/json + description: Update a discord integration for the currently authenticated user + parameters: + - default: 32343a19-da5e-4b1b-a767-3298a73703ca + description: ID of the discord integration + in: path + name: discordID + required: true + type: string + - description: Payload of discord integration to update + in: body + name: payload + required: true + schema: + $ref: "#/definitions/requests.DiscordUpdate" + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.DiscordResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Update a discord integration + tags: + - DiscordIntegration + /discord/event: + post: + consumes: + - application/json + description: Publish a discord event to the registered listeners + produces: + - application/json + responses: + "204": + description: No Content + schema: + $ref: "#/definitions/responses.NoContent" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + summary: Consume a discord event + tags: + - Discord + /heartbeats: + get: + consumes: + - application/json + description: + Get the last time a phone number requested for outstanding messages. + It will be sorted by timestamp in descending order. + parameters: + - default: "+18005550199" + description: the owner's phone number + in: query + name: owner + required: true + type: string + - description: number of heartbeats to skip + in: query + minimum: 0 + name: skip + type: integer + - description: filter containing query + in: query + name: query + type: string + - description: number of heartbeats to return + in: query + maximum: 20 + minimum: 1 + name: limit + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.HeartbeatsResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Get heartbeats of an owner phone number + tags: + - Heartbeats + post: + consumes: + - application/json + description: + Store the heartbeat to make notify that a phone number is still + active + parameters: + - description: Payload of the heartbeat request + in: body + name: payload + required: true + schema: + $ref: "#/definitions/requests.HeartbeatStore" + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.HeartbeatResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Register heartbeat of an owner phone number + tags: + - Heartbeats + /integration/3cx/messages: + post: + consumes: + - application/json + description: Sends an SMS message from the 3CX platform + produces: + - application/json + responses: + "204": + description: No Content + schema: + $ref: "#/definitions/responses.NoContent" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + summary: Sends a 3CX SMS message + tags: + - 3CXIntegration + /message-threads: + get: + consumes: + - application/json + description: + Get list of contacts which a phone number has communicated with + (threads). It will be sorted by timestamp in descending order. + parameters: + - default: "+18005550199" + description: owner phone number + in: query + name: owner + required: true + type: string + - description: number of messages to skip + in: query + minimum: 0 + name: skip + type: integer + - description: filter message threads containing query + in: query + name: query + type: string + - description: number of messages to return + in: query + maximum: 20 + minimum: 1 + name: limit + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.MessageThreadsResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Get message threads for a phone number + tags: + - MessageThreads + /message-threads/{messageThreadID}: + delete: + consumes: + - application/json + description: + Delete a message thread from the database and also deletes all + the messages in the thread. + parameters: + - default: 32343a19-da5e-4b1b-a767-3298a73703ca + description: ID of the message thread + in: path + name: messageThreadID + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + schema: + $ref: "#/definitions/responses.NoContent" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "404": + description: Not Found + schema: + $ref: "#/definitions/responses.NotFound" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Delete a message thread from the database. + tags: + - MessageThreads + put: + consumes: + - application/json + description: Updates the details of a message thread + parameters: + - default: 32343a19-da5e-4b1b-a767-3298a73703ca + description: ID of the message thread + in: path + name: messageThreadID + required: true + type: string + - description: Payload of message thread details to update + in: body + name: payload + required: true + schema: + $ref: "#/definitions/requests.MessageThreadUpdate" + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.PhoneResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Update a message thread + tags: + - MessageThreads + /messages: + get: + consumes: + - application/json + description: + Get list of messages which are sent between 2 phone numbers. It + will be sorted by timestamp in descending order. + parameters: + - default: "+18005550199" + description: the owner's phone number + in: query + name: owner + required: true + type: string + - default: "+18005550100" + description: the contact's phone number + in: query + name: contact + required: true + type: string + - description: number of messages to skip + in: query + minimum: 0 + name: skip + type: integer + - description: filter messages containing query + in: query + name: query + type: string + - description: number of messages to return + in: query + maximum: 20 + minimum: 1 + name: limit + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.MessagesResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Get messages which are sent between 2 phone numbers + tags: + - Messages + /messages/{messageID}: + delete: + consumes: + - application/json + description: + Delete a message from the database and removes the message content + from the list of threads. + parameters: + - default: 32343a19-da5e-4b1b-a767-3298a73703ca + description: ID of the message + in: path + name: messageID + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + schema: + $ref: "#/definitions/responses.NoContent" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "404": + description: Not Found + schema: + $ref: "#/definitions/responses.NotFound" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Delete a message from the database. + tags: + - Messages + get: + consumes: + - application/json + description: Get a message from the database by the message ID. + parameters: + - default: 32343a19-da5e-4b1b-a767-3298a73703ca + description: ID of the message + in: path + name: messageID + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + schema: + $ref: "#/definitions/responses.MessageResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "404": + description: Not Found + schema: + $ref: "#/definitions/responses.NotFound" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Get a message from the database. + tags: + - Messages + /messages/{messageID}/events: + post: + consumes: + - application/json + description: + Use this endpoint to send events for a message when it is failed, + sent or delivered by the mobile phone. + parameters: + - default: 32343a19-da5e-4b1b-a767-3298a73703ca + description: ID of the message + in: path + name: messageID + required: true + type: string + - description: Payload of the event emitted. + in: body + name: payload + required: true + schema: + $ref: "#/definitions/requests.MessageEvent" + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.MessageResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "404": + description: Not Found + schema: + $ref: "#/definitions/responses.NotFound" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Upsert an event for a message on the mobile phone + tags: + - Messages + /messages/bulk-send: + post: + consumes: + - application/json + description: Add bulk SMS messages to be sent by the android phone + parameters: + - description: Bulk send message request payload + in: body + name: payload + required: true + schema: + $ref: "#/definitions/requests.MessageBulkSend" + produces: + - application/json + responses: + "200": + description: OK + schema: + items: + $ref: "#/definitions/responses.MessagesResponse" + type: array + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Send bulk SMS messages + tags: + - Messages + /messages/calls/missed: + post: + consumes: + - application/json + description: + This endpoint is called by the httpSMS android app to register + a missed call event on the mobile phone. + parameters: + - description: Payload of the missed call event. + in: body + name: payload + required: true + schema: + $ref: "#/definitions/requests.MessageCallMissed" + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.MessageResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "404": + description: Not Found + schema: + $ref: "#/definitions/responses.NotFound" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Register a missed call event on the mobile phone + tags: + - Messages + /messages/outstanding: + get: + consumes: + - application/json + description: Get an outstanding message to be sent by an android phone + parameters: + - default: 32343a19-da5e-4b1b-a767-3298a73703cb + description: The ID of the message + in: query + name: message_id + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.MessageResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Get an outstanding message + tags: + - Messages + /messages/receive: + post: + consumes: + - application/json + description: Add a new message received from a mobile phone + parameters: + - description: Received message request payload + in: body + name: payload + required: true + schema: + $ref: "#/definitions/requests.MessageReceive" + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.MessageResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Receive a new SMS message from a mobile phone + tags: + - Messages + /messages/search: + get: + consumes: + - application/json + description: + This returns the list of all messages based on the filter criteria + including missed calls + parameters: + - description: Cloudflare turnstile token https://www.cloudflare.com/en-gb/application-services/products/turnstile/ + in: header + name: token + required: true + type: string + - default: +18005550199,+18005550100 + description: the owner's phone numbers + in: query + name: owners + required: true + type: string + - description: number of messages to skip + in: query + minimum: 0 + name: skip + type: integer + - description: filter messages containing query + in: query + name: query + type: string + - description: number of messages to return + in: query + maximum: 200 + minimum: 1 + name: limit + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.MessagesResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Search all messages of a user + tags: + - Messages + /messages/send: + post: + consumes: + - application/json + description: Add a new SMS message to be sent by your Android phone + parameters: + - description: Send message request payload + in: body + name: payload + required: true + schema: + $ref: "#/definitions/requests.MessageSend" + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.MessageResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Send an SMS message + tags: + - Messages + /phone-api-keys: + get: + consumes: + - application/json + description: + Get list phone API keys which a user has registered on the httpSMS + application + parameters: + - description: number of phone api keys to skip + in: query + minimum: 0 + name: skip + type: integer + - description: filter phone api keys with name containing query + in: query + name: query + type: string + - description: number of phone api keys to return + in: query + maximum: 100 + minimum: 1 + name: limit + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.PhoneAPIKeysResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Get the phone API keys of a user + tags: + - PhoneAPIKeys + post: + consumes: + - application/json + description: + Creates a new phone API key which can be used to log in to the + httpSMS app on your Android phone + parameters: + - description: Payload of new phone API key. + in: body + name: payload + required: true + schema: + $ref: "#/definitions/requests.PhoneAPIKeyStoreRequest" + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.PhoneAPIKeyResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Store phone API key + tags: + - PhoneAPIKeys + /phone-api-keys/{phoneAPIKeyID}: + delete: + consumes: + - application/json + description: + Delete a phone API Key from the database and cannot be used for + authentication anymore. + parameters: + - default: 32343a19-da5e-4b1b-a767-3298a73703ca + description: ID of the phone API key + in: path + name: phoneAPIKeyID + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + schema: + $ref: "#/definitions/responses.NoContent" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "404": + description: Not Found + schema: + $ref: "#/definitions/responses.NotFound" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Delete a phone API key from the database. + tags: + - PhoneAPIKeys + /phone-api-keys/{phoneAPIKeyID}/phones/{phoneID}: + delete: + consumes: + - application/json + description: + You will need to login again to the httpSMS app on your Android + phone with a new phone API key. + parameters: + - default: 32343a19-da5e-4b1b-a767-3298a73703ca + description: ID of the phone API key + in: path + name: phoneAPIKeyID + required: true + type: string + - default: 32343a19-da5e-4b1b-a767-3298a73703ca + description: ID of the phone + in: path + name: phoneID + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + schema: + $ref: "#/definitions/responses.NoContent" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "404": + description: Not Found + schema: + $ref: "#/definitions/responses.NotFound" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Remove the association of a phone from the phone API key. + tags: + - PhoneAPIKeys + /phones: + get: + consumes: + - application/json + description: + Get list of phones which a user has registered on the http sms + application + parameters: + - description: number of heartbeats to skip + in: query + minimum: 0 + name: skip + type: integer + - description: filter phones containing query + in: query + name: query + type: string + - description: number of phones to return + in: query + maximum: 20 + minimum: 1 + name: limit + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.PhonesResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Get phones of a user + tags: + - Phones + put: + consumes: + - application/json + description: + Updates properties of a user's phone. If the phone with this number + does not exist, a new one will be created. Think of this method like an 'upsert' + parameters: + - description: Payload of new phone number. + in: body + name: payload + required: true + schema: + $ref: "#/definitions/requests.PhoneUpsert" + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.PhoneResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Upsert Phone + tags: + - Phones + /phones/{phoneID}: + delete: + consumes: + - application/json + description: Delete a phone that has been sored in the database + parameters: + - default: 32343a19-da5e-4b1b-a767-3298a73703ca + description: ID of the phone + in: path + name: phoneID + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + schema: + $ref: "#/definitions/responses.NoContent" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Delete Phone + tags: + - Phones + /phones/fcm-token: + put: + consumes: + - application/json + description: + Updates the FCM token of a phone. If the phone with this number + does not exist, a new one will be created. Think of this method like an 'upsert' + parameters: + - description: Payload of new FCM token. + in: body + name: payload + required: true + schema: + $ref: "#/definitions/requests.PhoneFCMToken" + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.PhoneResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Upserts the FCM token of a phone + tags: + - Phones + /users/{userID}/api-keys: + delete: + consumes: + - application/json + description: Rotate the user's API key in case the current API Key is compromised + parameters: + - default: 32343a19-da5e-4b1b-a767-3298a73703ca + description: ID of the user to update + in: path + name: userID + required: true + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.UserResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Rotate the user's API Key + tags: + - Users + /users/{userID}/notifications: + put: + consumes: + - application/json + description: Update the email notification settings for a user + parameters: + - default: 32343a19-da5e-4b1b-a767-3298a73703ca + description: ID of the user to update + in: path + name: userID + required: true + type: string + - description: User notification details to update + in: body + name: payload + required: true + schema: + $ref: "#/definitions/requests.UserNotificationUpdate" + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.UserResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Update notification settings + tags: + - Users + /users/me: + delete: + consumes: + - application/json + description: + Deletes the currently authenticated user together with all their + data. + produces: + - application/json + responses: + "201": + description: Created + schema: + $ref: "#/definitions/responses.NoContent" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Delete a user + tags: + - Users + get: + consumes: + - application/json + description: Get details of the currently authenticated user + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.UserResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Get current user + tags: + - Users + put: + consumes: + - application/json + description: Updates the details of the currently authenticated user + parameters: + - description: Payload of user details to update + in: body + name: payload + required: true + schema: + $ref: "#/definitions/requests.UserUpdate" + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.PhoneResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Update a user + tags: + - Users + /users/subscription: + delete: + description: Cancel the subscription of the authenticated user. + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.NoContent" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Cancel the user's subscription + tags: + - Users + /users/subscription-update-url: + get: + description: Fetches the subscription URL of the authenticated user. + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.OkString" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Currently authenticated user subscription update URL + tags: + - Users + /users/subscription/invoices/{subscriptionInvoiceID}: + post: + consumes: + - application/json + description: + Generates a new invoice PDF file for the given subscription payment + with given parameters. + parameters: + - description: Generate subscription payment invoice parameters + in: body + name: payload + required: true + schema: + $ref: "#/definitions/requests.UserPaymentInvoice" + - description: ID of the subscription invoice to generate the PDF for + in: path + name: subscriptionInvoiceID + required: true + type: string + produces: + - application/pdf + responses: + "200": + description: OK + schema: + type: file + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Generate a subscription payment invoice + tags: + - Users + /users/subscription/payments: + get: + consumes: + - application/json + description: + Subscription payments are generated throughout the lifecycle of + a subscription, typically there is one at the time of purchase and then one + for each renewal. + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.UserSubscriptionPaymentsResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Get the last 10 subscription payments. + tags: + - Users + /v1/attachments/{userID}/{messageID}/{attachmentIndex}/{filename}: + get: + description: Download an MMS attachment by its path components + parameters: + - description: User ID + in: path + name: userID + required: true + type: string + - description: Message ID + in: path + name: messageID + required: true + type: string + - description: Attachment index + in: path + name: attachmentIndex + required: true + type: string + - description: Filename with extension + in: path + name: filename + required: true + type: string + produces: + - application/octet-stream + responses: + "200": + description: OK + schema: + type: file + "404": + description: Not Found + schema: + $ref: "#/definitions/responses.NotFound" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + summary: Download a message attachment + tags: + - Attachments + /webhooks: + get: + consumes: + - application/json + description: Get the webhooks of a user + parameters: + - description: number of webhooks to skip + in: query + minimum: 0 + name: skip + type: integer + - description: filter webhooks containing query + in: query + name: query + type: string + - description: number of webhooks to return + in: query + maximum: 20 + minimum: 1 + name: limit + type: integer + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.WebhooksResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Get webhooks of a user + tags: + - Webhooks + post: + consumes: + - application/json + description: Store a webhook for the authenticated user + parameters: + - description: Payload of the webhook request + in: body + name: payload + required: true + schema: + $ref: "#/definitions/requests.WebhookStore" + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.WebhookResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Store a webhook + tags: + - Webhooks + /webhooks/{webhookID}: + delete: + consumes: + - application/json + description: Delete a webhook for a user + parameters: + - default: 32343a19-da5e-4b1b-a767-3298a73703ca + description: ID of the webhook + in: path + name: webhookID + required: true + type: string + produces: + - application/json + responses: + "204": + description: No Content + schema: + $ref: "#/definitions/responses.NoContent" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Delete webhook + tags: + - Webhooks + put: + consumes: + - application/json + description: Update a webhook for the currently authenticated user + parameters: + - default: 32343a19-da5e-4b1b-a767-3298a73703ca + description: ID of the webhook + in: path + name: webhookID + required: true + type: string + - description: Payload of webhook details to update + in: body + name: payload + required: true + schema: + $ref: "#/definitions/requests.WebhookUpdate" + produces: + - application/json + responses: + "200": + description: OK + schema: + $ref: "#/definitions/responses.WebhookResponse" + "400": + description: Bad Request + schema: + $ref: "#/definitions/responses.BadRequest" + "401": + description: Unauthorized + schema: + $ref: "#/definitions/responses.Unauthorized" + "422": + description: Unprocessable Entity + schema: + $ref: "#/definitions/responses.UnprocessableEntity" + "500": + description: Internal Server Error + schema: + $ref: "#/definitions/responses.InternalServerError" + security: + - ApiKeyAuth: [] + summary: Update a webhook + tags: + - Webhooks +schemes: + - https +securityDefinitions: + ApiKeyAuth: + in: header + name: x-api-Key + type: apiKey +swagger: "2.0" diff --git a/api/go.mod b/api/go.mod index b6246c13..3c361930 100644 --- a/api/go.mod +++ b/api/go.mod @@ -1,157 +1,206 @@ module github.com/NdoleStudio/httpsms -go 1.18 +go 1.25.0 require ( - cloud.google.com/go/cloudtasks v1.12.4 + cloud.google.com/go/cloudtasks v1.14.0 + cloud.google.com/go/storage v1.62.0 firebase.google.com/go v3.13.0+incompatible - github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.44.0 - github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.20.0 - github.com/NdoleStudio/go-otelroundtripper v0.0.9 - github.com/NdoleStudio/lemonsqueezy-go v1.0.3 - github.com/avast/retry-go v3.0.0+incompatible - github.com/carlmjohnson/requests v0.23.5 - github.com/cloudevents/sdk-go/v2 v2.14.0 - github.com/cockroachdb/cockroach-go/v2 v2.3.5 - github.com/davecgh/go-spew v1.1.1 - github.com/dgraph-io/ristretto v0.1.1 + github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 + github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.31.0 + github.com/NdoleStudio/go-otelroundtripper v0.0.14 + github.com/NdoleStudio/lemonsqueezy-go v1.3.1 + github.com/NdoleStudio/plunk-go v0.0.2 + github.com/avast/retry-go/v5 v5.0.0 + github.com/carlmjohnson/requests v0.25.1 + github.com/cloudevents/sdk-go/v2 v2.16.2 + github.com/cockroachdb/cockroach-go/v2 v2.4.3 + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc + github.com/dgraph-io/ristretto/v2 v2.4.0 github.com/dustin/go-humanize v1.0.1 + github.com/go-hermes/hermes/v2 v2.6.2 github.com/gofiber/contrib/otelfiber v1.0.10 - github.com/gofiber/fiber/v2 v2.50.0 - github.com/gofiber/swagger v0.1.14 - github.com/golang-jwt/jwt v3.2.2+incompatible - github.com/google/uuid v1.4.0 - github.com/hashicorp/go-retryablehttp v0.7.5 + github.com/gofiber/fiber/v2 v2.52.12 + github.com/gofiber/swagger v1.1.1 + github.com/golang-jwt/jwt/v5 v5.3.1 + github.com/google/uuid v1.6.0 + github.com/hashicorp/go-retryablehttp v0.7.8 github.com/hirosassa/zerodriver v0.1.4 + github.com/jaswdr/faker/v2 v2.9.1 github.com/jinzhu/now v1.1.5 github.com/joho/godotenv v1.5.1 github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible - github.com/jszwec/csvutil v1.8.0 - github.com/lib/pq v1.10.9 - github.com/matcornic/hermes/v2 v2.1.0 - github.com/nyaruka/phonenumbers v1.1.9 + github.com/jszwec/csvutil v1.10.0 + github.com/lib/pq v1.12.2 + github.com/nyaruka/phonenumbers v1.7.1 github.com/palantir/stacktrace v0.0.0-20161112013806-78658fd2d177 + github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pkg/errors v0.9.1 - github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 - github.com/redis/go-redis/v9 v9.3.0 - github.com/rs/zerolog v1.31.0 - github.com/sendgrid/sendgrid-go v3.13.0+incompatible - github.com/stretchr/testify v1.8.4 - github.com/swaggo/swag v1.16.2 + github.com/pusher/pusher-http-go/v5 v5.1.1 + github.com/redis/go-redis/extra/redisotel/v9 v9.18.0 + github.com/redis/go-redis/v9 v9.18.0 + github.com/rs/zerolog v1.35.0 + github.com/stretchr/testify v1.11.1 + github.com/swaggo/swag v1.16.6 github.com/thedevsaddam/govalidator v1.9.10 - github.com/uptrace/uptrace-go v1.20.2 - github.com/xuri/excelize/v2 v2.8.0 - go.opentelemetry.io/otel v1.20.0 - go.opentelemetry.io/otel/metric v1.20.0 - go.opentelemetry.io/otel/sdk v1.20.0 - go.opentelemetry.io/otel/sdk/metric v1.20.0 - go.opentelemetry.io/otel/trace v1.20.0 - google.golang.org/api v0.150.0 - google.golang.org/protobuf v1.31.0 - gorm.io/datatypes v1.2.0 - gorm.io/driver/postgres v1.5.4 - gorm.io/gorm v1.25.5 - gorm.io/plugin/opentelemetry v0.1.4 + github.com/tursodatabase/libsql-client-go v0.0.0-20251219100830-236aa1ff8acc + github.com/uptrace/uptrace-go v1.41.1 + github.com/xuri/excelize/v2 v2.10.1 + go.opentelemetry.io/otel v1.43.0 + go.opentelemetry.io/otel/metric v1.43.0 + go.opentelemetry.io/otel/sdk v1.43.0 + go.opentelemetry.io/otel/sdk/metric v1.43.0 + go.opentelemetry.io/otel/trace v1.43.0 + golang.org/x/sync v0.20.0 + google.golang.org/api v0.274.0 + google.golang.org/protobuf v1.36.11 + gorm.io/driver/postgres v1.6.0 + gorm.io/driver/sqlite v1.6.0 + gorm.io/gorm v1.31.1 + gorm.io/plugin/opentelemetry v0.1.16 ) require ( - cloud.google.com/go v0.110.10 // indirect - cloud.google.com/go/compute v1.23.3 // indirect - cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/firestore v1.14.0 // indirect - cloud.google.com/go/iam v1.1.5 // indirect - cloud.google.com/go/longrunning v0.5.4 // indirect - cloud.google.com/go/monitoring v1.16.3 // indirect - cloud.google.com/go/storage v1.35.1 // indirect - cloud.google.com/go/trace v1.10.4 // indirect - github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.44.0 // indirect + github.com/Masterminds/semver/v3 v3.4.0 // indirect + github.com/Masterminds/sprig/v3 v3.3.0 // indirect + github.com/inbucket/html2text v1.0.0 // indirect + github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect + github.com/olekukonko/errors v1.2.0 // indirect + github.com/olekukonko/ll v0.1.8 // indirect + github.com/sirupsen/logrus v1.9.4 // indirect + github.com/spf13/cast v1.10.0 // indirect + github.com/yuin/goldmark v1.8.2 // indirect +) + +require ( + cel.dev/expr v0.25.1 // indirect + cloud.google.com/go v0.123.0 // indirect + cloud.google.com/go/auth v0.19.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect + cloud.google.com/go/compute/metadata v0.9.0 // indirect + cloud.google.com/go/firestore v1.21.0 // indirect + cloud.google.com/go/iam v1.7.0 // indirect + cloud.google.com/go/longrunning v0.9.0 // indirect + cloud.google.com/go/monitoring v1.25.0 // indirect + cloud.google.com/go/trace v1.12.0 // indirect + dario.cat/mergo v1.0.2 // indirect + filippo.io/edwards25519 v1.2.0 // indirect + github.com/ClickHouse/ch-go v0.71.0 // indirect + github.com/ClickHouse/clickhouse-go/v2 v2.44.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 // indirect + github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 // indirect github.com/KyleBanks/depth v1.2.1 // indirect github.com/Masterminds/goutils v1.1.1 // indirect - github.com/Masterminds/semver v1.5.0 // indirect - github.com/Masterminds/sprig v2.22.0+incompatible // indirect - github.com/PuerkitoBio/goquery v1.8.1 // indirect - github.com/andybalholm/brotli v1.0.6 // indirect - github.com/andybalholm/cascadia v1.3.2 // indirect - github.com/cenkalti/backoff/v4 v4.2.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/PuerkitoBio/goquery v1.12.0 // indirect + github.com/andybalholm/brotli v1.2.1 // indirect + github.com/andybalholm/cascadia v1.3.3 // indirect + github.com/antlr4-go/antlr/v4 v4.13.1 // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/clipperhouse/displaywidth v0.11.0 // indirect + github.com/clipperhouse/uax29/v2 v2.7.0 // indirect + github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect + github.com/coder/websocket v1.8.14 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/go-logr/logr v1.3.0 // indirect + github.com/envoyproxy/go-control-plane/envoy v1.37.0 // indirect + github.com/envoyproxy/protoc-gen-validate v1.3.3 // indirect + github.com/fatih/color v1.19.0 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-faster/city v1.0.1 // indirect + github.com/go-faster/errors v0.7.1 // indirect + github.com/go-jose/go-jose/v4 v4.1.4 // indirect + github.com/go-logr/logr v1.4.3 // indirect github.com/go-logr/stdr v1.2.2 // indirect - github.com/go-openapi/jsonpointer v0.20.0 // indirect - github.com/go-openapi/jsonreference v0.20.2 // indirect - github.com/go-openapi/spec v0.20.9 // indirect - github.com/go-openapi/swag v0.22.4 // indirect - github.com/go-sql-driver/mysql v1.7.1 // indirect - github.com/golang/glog v1.1.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/google/s2a-go v0.1.7 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect - github.com/googleapis/gax-go/v2 v2.12.0 // indirect + github.com/go-openapi/jsonpointer v0.22.5 // indirect + github.com/go-openapi/jsonreference v0.21.5 // indirect + github.com/go-openapi/spec v0.22.4 // indirect + github.com/go-openapi/swag/conv v0.25.5 // indirect + github.com/go-openapi/swag/jsonname v0.25.5 // indirect + github.com/go-openapi/swag/jsonutils v0.25.5 // indirect + github.com/go-openapi/swag/loading v0.25.5 // indirect + github.com/go-openapi/swag/stringutils v0.25.5 // indirect + github.com/go-openapi/swag/typeutils v0.25.5 // indirect + github.com/go-openapi/swag/yamlutils v0.25.5 // indirect + github.com/go-sql-driver/mysql v1.9.3 // indirect + github.com/goccy/go-json v0.10.6 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/google/s2a-go v0.1.9 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect + github.com/googleapis/gax-go/v2 v2.21.0 // indirect github.com/gorilla/css v1.0.1 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect github.com/hashicorp/go-cleanhttp v0.5.2 // indirect - github.com/huandu/xstrings v1.4.0 // indirect - github.com/imdario/mergo v0.3.16 // indirect + github.com/hashicorp/go-version v1.9.0 // indirect + github.com/huandu/xstrings v1.5.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect - github.com/jackc/pgx/v5 v5.5.0 // indirect - github.com/jackc/puddle/v2 v2.2.1 // indirect - github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.9.2 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect github.com/jinzhu/inflection v1.0.0 // indirect - github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.2 // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/mattn/go-colorable v0.1.13 // indirect + github.com/klauspost/compress v1.18.5 // indirect + github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.15 // indirect + github.com/mattn/go-runewidth v0.0.22 // indirect + github.com/mattn/go-sqlite3 v1.14.39 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect - github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect - github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 // indirect - github.com/richardlehane/mscfb v1.0.4 // indirect - github.com/richardlehane/msoleps v1.0.3 // indirect - github.com/rivo/uniseg v0.4.4 // indirect - github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/sendgrid/rest v2.6.9+incompatible // indirect + github.com/olekukonko/tablewriter v1.1.4 // indirect + github.com/paulmach/orb v0.13.0 // indirect + github.com/pierrec/lz4/v4 v4.1.26 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/redis/go-redis/extra/rediscmd/v9 v9.18.0 // indirect + github.com/richardlehane/mscfb v1.0.6 // indirect + github.com/richardlehane/msoleps v1.0.6 // indirect + github.com/segmentio/asm v1.2.1 // indirect + github.com/shopspring/decimal v1.4.0 // indirect + github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf // indirect - github.com/swaggo/files/v2 v2.0.0 // indirect + github.com/swaggo/files/v2 v2.0.2 // indirect + github.com/tiendc/go-deepcopy v1.7.2 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect - github.com/valyala/fasthttp v1.50.0 // indirect - github.com/valyala/tcplisten v1.0.0 // indirect + github.com/valyala/fasthttp v1.69.0 // indirect github.com/vanng822/css v1.0.1 // indirect - github.com/vanng822/go-premailer v1.20.2 // indirect - github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53 // indirect - github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05 // indirect - go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/contrib v1.21.0 // indirect - go.opentelemetry.io/contrib/instrumentation/runtime v0.46.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.43.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0 // indirect - go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.20.0 // indirect - go.opentelemetry.io/proto/otlp v1.0.0 // indirect + github.com/vanng822/go-premailer v1.33.0 // indirect + github.com/xuri/efp v0.0.1 // indirect + github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib v1.42.0 // indirect + go.opentelemetry.io/contrib/detectors/gcp v1.42.0 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect + go.opentelemetry.io/contrib/instrumentation/runtime v0.67.0 // indirect + go.opentelemetry.io/contrib/processors/minsev v0.15.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 // indirect + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 // indirect + go.opentelemetry.io/otel/log v0.19.0 // indirect + go.opentelemetry.io/otel/sdk/log v0.19.0 // indirect + go.opentelemetry.io/proto/otlp v1.10.0 // indirect + go.uber.org/atomic v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.26.0 // indirect - golang.org/x/crypto v0.15.0 // indirect - golang.org/x/net v0.18.0 // indirect - golang.org/x/oauth2 v0.14.0 // indirect - golang.org/x/sync v0.5.0 // indirect - golang.org/x/sys v0.14.0 // indirect - golang.org/x/text v0.14.0 // indirect - golang.org/x/time v0.4.0 // indirect - golang.org/x/tools v0.15.0 // indirect - golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect + go.uber.org/zap v1.27.1 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/crypto v0.49.0 // indirect + golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 // indirect + golang.org/x/mod v0.34.0 // indirect + golang.org/x/net v0.52.0 // indirect + golang.org/x/oauth2 v0.36.0 // indirect + golang.org/x/sys v0.42.0 // indirect + golang.org/x/text v0.35.0 // indirect + golang.org/x/time v0.15.0 // indirect + golang.org/x/tools v0.43.0 // indirect google.golang.org/appengine v1.6.8 // indirect - google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect - google.golang.org/grpc v1.59.0 // indirect + google.golang.org/genproto v0.0.0-20260401024825-9d38bb4040a9 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect + google.golang.org/grpc v1.80.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - gorm.io/driver/mysql v1.5.2 // indirect + gorm.io/driver/clickhouse v0.7.0 // indirect + gorm.io/driver/mysql v1.6.0 // indirect ) diff --git a/api/go.sum b/api/go.sum index c4bb01e1..60df8733 100644 --- a/api/go.sum +++ b/api/go.sum @@ -1,214 +1,224 @@ bou.ke/monkey v1.0.2 h1:kWcnsrCNUatbxncxR/ThdYqbytgOIArtYWqcQLQzKLI= -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.110.10 h1:LXy9GEO+timppncPIAZoOj3l58LIU9k+kn48AN7IO3Y= -cloud.google.com/go v0.110.10/go.mod h1:v1OoFqYxiBkUrruItNM3eT4lLByNjxmJSV/xDKJNnic= -cloud.google.com/go/cloudtasks v1.12.4 h1:5xXuFfAjg0Z5Wb81j2GAbB3e0bwroCeSF+5jBn/L650= -cloud.google.com/go/cloudtasks v1.12.4/go.mod h1:BEPu0Gtt2dU6FxZHNqqNdGqIG86qyWKBPGnsb7udGY0= -cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiVlk= -cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/firestore v1.14.0 h1:8aLcKnMPoldYU3YHgu4t2exrKhLQkqaXAGqT0ljrFVw= -cloud.google.com/go/firestore v1.14.0/go.mod h1:96MVaHLsEhbvkBEdZgfN+AS/GIkco1LRpH9Xp9YZfzQ= -cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI= -cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= -cloud.google.com/go/logging v1.8.1 h1:26skQWPeYhvIasWKm48+Eq7oUqdcdbwsCVwz5Ys0FvU= -cloud.google.com/go/longrunning v0.5.4 h1:w8xEcbZodnA2BbW6sVirkkoC+1gP8wS57EUUgGS0GVg= -cloud.google.com/go/longrunning v0.5.4/go.mod h1:zqNVncI0BOP8ST6XQD1+VcvuShMmq7+xFSzOL++V0dI= -cloud.google.com/go/monitoring v1.16.3 h1:mf2SN9qSoBtIgiMA4R/y4VADPWZA7VCNJA079qLaZQ8= -cloud.google.com/go/monitoring v1.16.3/go.mod h1:KwSsX5+8PnXv5NJnICZzW2R8pWTis8ypC4zmdRD63Tw= -cloud.google.com/go/storage v1.35.1 h1:B59ahL//eDfx2IIKFBeT5Atm9wnNmj3+8xG/W4WB//w= -cloud.google.com/go/storage v1.35.1/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= -cloud.google.com/go/trace v1.10.4 h1:2qOAuAzNezwW3QN+t41BtkDJOG42HywL73q8x/f6fnM= -cloud.google.com/go/trace v1.10.4/go.mod h1:Nso99EDIK8Mj5/zmB+iGr9dosS/bzWCJ8wGmE6TXNWY= +bou.ke/monkey v1.0.2/go.mod h1:OqickVX3tNx6t33n1xvtTtu85YN5s6cKwVug+oHMaIA= +cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= +cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= +cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE= +cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU= +cloud.google.com/go/auth v0.19.0 h1:DGYwtbcsGsT1ywuxsIoWi1u/vlks0moIblQHgSDgQkQ= +cloud.google.com/go/auth v0.19.0/go.mod h1:2Aph7BT2KnaSFOM0JDPyiYgNh6PL9vGMiP8CUIXZ+IY= +cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc= +cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c= +cloud.google.com/go/cloudtasks v1.14.0 h1:l+9VVqB6Bbpn1NhYBwn9TMs5Yu7jU0bSfd9mrRilt48= +cloud.google.com/go/cloudtasks v1.14.0/go.mod h1:mFzsLKuM4gzzmlbu1363510Fjm5ZJR+8mH1C2w5roJo= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= +cloud.google.com/go/firestore v1.21.0 h1:BhopUsx7kh6NFx77ccRsHhrtkbJUmDAxNY3uapWdjcM= +cloud.google.com/go/firestore v1.21.0/go.mod h1:1xH6HNcnkf/gGyR8udd6pFO4Z7GWJSwLKQMx/u6UrP4= +cloud.google.com/go/iam v1.7.0 h1:JD3zh0C6LHl16aCn5Akff0+GELdp1+4hmh6ndoFLl8U= +cloud.google.com/go/iam v1.7.0/go.mod h1:tetWZW1PD/m6vcuY2Zj/aU0eCHNPuxedbnbRTyKXvdY= +cloud.google.com/go/logging v1.13.2 h1:qqlHCBvieJT9Cdq4QqYx1KPadCQ2noD4FK02eNqHAjA= +cloud.google.com/go/logging v1.13.2/go.mod h1:zaybliM3yun1J8mU2dVQ1/qDzjbOqEijZCn6hSBtKak= +cloud.google.com/go/longrunning v0.9.0 h1:0EzbDEGsAvOZNbqXopgniY0w0a1phvu5IdUFq8grmqY= +cloud.google.com/go/longrunning v0.9.0/go.mod h1:pkTz846W7bF4o2SzdWJ40Hu0Re+UoNT6Q5t+igIcb8E= +cloud.google.com/go/monitoring v1.25.0 h1:HnsTIOxTN6BCSkt1P/Im23r1m7MHTTpmSYCzPkW7NK4= +cloud.google.com/go/monitoring v1.25.0/go.mod h1:wlj6rX+JGyusw/8+2duW4cJ6kmDHGmde3zMTJuG3Jpc= +cloud.google.com/go/storage v1.62.0 h1:w2pQJhpUqVerMON45vatE2FpCYsNTf7OHjkn6ux5mMU= +cloud.google.com/go/storage v1.62.0/go.mod h1:T5hz3qzcpnxZ5LdKc7y8Tw7lh4v9zeeVyrD/cLJAzZU= +cloud.google.com/go/trace v1.12.0 h1:XvWHYfr9q88cX4pZyou6qCcSagnuASyUq2ej1dB6NzQ= +cloud.google.com/go/trace v1.12.0/go.mod h1:TOYfyeoyCGsSH0ifXD6Aius24uQI9xV3RyvOdljFIyg= +dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= +dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= +filippo.io/edwards25519 v1.2.0 h1:crnVqOiS4jqYleHd9vaKZ+HKtHfllngJIiOpNpoJsjo= +filippo.io/edwards25519 v1.2.0/go.mod h1:xzAOLCNug/yB62zG1bQ8uziwrIqIuxhctzJT18Q77mc= firebase.google.com/go v3.13.0+incompatible h1:3TdYC3DDi6aHn20qoRkxwGqNgdjtblwVAyRLQwGn/+4= firebase.google.com/go v3.13.0+incompatible/go.mod h1:xlah6XbEyW6tbfSklcfe5FHJIwjt8toICdV5Wh9ptHs= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.44.0 h1:TKInLYWIqv/RrPM9IbpbcAxmtAU0+EtrxbZGZn2pzfQ= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.44.0/go.mod h1:shFWgjEP9WVKRUJbgyp61kOiFAd0AUPUyy16fgyhJ5Q= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.20.0 h1:uY/4lpbbFG73TgzmJoB7XMyFIheII95hlfH62uC+oS0= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.20.0/go.mod h1:qaUEgkhkSlCNIu9/XD4y19vnbwKskfz2ep6Utf2A57c= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.44.0 h1:ew7SfeajMJ3I4iXA1LERYY62fGCKO4TjVPw5QTPt47k= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.44.0 h1:GjWPDY9PUlNWwTI95L/lktUp35BLtzBoBElH314eafM= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.44.0/go.mod h1:qkFPtMouQjW5ugdHIOthiTbweVHUTqbS0Qsu55KqXks= +github.com/ClickHouse/ch-go v0.71.0 h1:bUdZ/EZj/LcVHsMqaRUP2holqygrPWQKeMjc6nZoyRM= +github.com/ClickHouse/ch-go v0.71.0/go.mod h1:NwbNc+7jaqfY58dmdDUbG4Jl22vThgx1cYjBw0vtgXw= +github.com/ClickHouse/clickhouse-go/v2 v2.44.0 h1:9pxs5pRwIvhni5BDRPn/n5A8DeUod5TnBaeulFBX8EQ= +github.com/ClickHouse/clickhouse-go/v2 v2.44.0/go.mod h1:giJfUVlMkcfUEPVfRpt51zZaGEx9i17gCos8gBl392c= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 h1:DHa2U07rk8syqvCge0QIGMCE1WxGj9njT44GH7zNJLQ= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0 h1:UnDZ/zFfG1JhH/DqxIZYU/1CUAlTUScoXD/LcM2Ykk8= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.55.0/go.mod h1:IA1C1U7jO/ENqm/vhi7V9YYpBsp+IMyqNrEN94N7tVc= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.31.0 h1:xQMhkBXPOKe/GzC6TctwlK2aNF+9k5VwFgdE83rBK2Y= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace v1.31.0/go.mod h1:VLoD5cAsRQXsAFXpOZrrTGzbuMsntlspIZno4xor5Zg= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.55.0 h1:7t/qx5Ost0s0wbA/VDrByOooURhp+ikYwv20i9Y07TQ= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.55.0/go.mod h1:vB2GH9GAYYJTO3mEn8oYwzEdhlayZIdQz6zdzgUIRvA= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0 h1:0s6TxfCu2KHkkZPnBfsQ2y5qia0jl3MMrmBhu3nCOYk= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.55.0/go.mod h1:Mf6O40IAyB9zR/1J8nGDDPirZQQPbYJni8Yisy7NTMc= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= -github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= -github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= -github.com/Masterminds/sprig v2.16.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= -github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= -github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= -github.com/NdoleStudio/go-otelroundtripper v0.0.9 h1:wbkmz2UOz4TNKnOs15aODqb2THcF0wFoB3C9wdUln8Y= -github.com/NdoleStudio/go-otelroundtripper v0.0.9/go.mod h1:6SLgW5Bh6dRW3XyYlfqEOzFqak848uv6n1+RaZa3LHc= -github.com/NdoleStudio/lemonsqueezy-go v1.0.3 h1:WAENkeTfKDyE83jIP1+HWoF54lV5/ryadREnlMLDs7Y= -github.com/NdoleStudio/lemonsqueezy-go v1.0.3/go.mod h1:yRYTtU1RyapMDPTFKypIEKLEjPAbWq9a5PGqqvIn6/Y= -github.com/PuerkitoBio/goquery v1.5.0/go.mod h1:qD2PgZ9lccMbQlc7eEOjaeRlFQON7xY8kdmcsrnKqMg= -github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc= -github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= -github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI= -github.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= -github.com/andybalholm/cascadia v1.0.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= -github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y= -github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= -github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= -github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= -github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ= -github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= -github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= -github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs= +github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= +github.com/NdoleStudio/go-otelroundtripper v0.0.14 h1:t/VoW2772wTDQnjdECxxWbtZtbnpJyuRSKxRC/hHfTg= +github.com/NdoleStudio/go-otelroundtripper v0.0.14/go.mod h1:ObQjHo1D/daXeESbFIi0UXJN0yJu4zQ7mMeSKvm4a1I= +github.com/NdoleStudio/lemonsqueezy-go v1.3.1 h1:lMUVgdAx2onbOUJIVPR05xAANYuCMXBRaGWpAdA4LiM= +github.com/NdoleStudio/lemonsqueezy-go v1.3.1/go.mod h1:xKRsRX1jSI6mLrVXyWh2sF/1isxTioZrSjWy6HpA3xQ= +github.com/NdoleStudio/plunk-go v0.0.2 h1:afPW7MHK4Z3rsybpJBnmTmxKCLKF1M7sPI+BNGPf35A= +github.com/NdoleStudio/plunk-go v0.0.2/go.mod h1:pqG3zKhpn/A2bL1K+WsWzvfTpOeSkYgXhNk5H65uEc8= +github.com/PuerkitoBio/goquery v1.12.0 h1:pAcL4g3WRXekcB9AU/y1mbKez2dbY2AajVhtkO8RIBo= +github.com/PuerkitoBio/goquery v1.12.0/go.mod h1:802ej+gV2y7bbIhOIoPY5sT183ZW0YFofScC4q/hIpQ= +github.com/andybalholm/brotli v1.2.1 h1:R+f5xP285VArJDRgowrfb9DqL18yVK0gKAW/F+eTWro= +github.com/andybalholm/brotli v1.2.1/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= +github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM= +github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA= +github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= +github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= +github.com/avast/retry-go/v5 v5.0.0 h1:kf1Qc2UsTZ4qq8elDymqfbISvkyMuhgRxuJqX2NHP7k= +github.com/avast/retry-go/v5 v5.0.0/go.mod h1://d+usmKWio1agtZfS1H/ltTqwtIfBnRq9zEwjc3eH8= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= -github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= -github.com/carlmjohnson/requests v0.23.5 h1:NPANcAofwwSuC6SIMwlgmHry2V3pLrSqRiSBKYbNHHA= -github.com/carlmjohnson/requests v0.23.5/go.mod h1:zG9P28thdRnN61aD7iECFhH5iGGKX2jIjKQD9kqYH+o= -github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= -github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cloudevents/sdk-go/v2 v2.14.0 h1:Nrob4FwVgi5L4tV9lhjzZcjYqFVyJzsA56CwPaPfv6s= -github.com/cloudevents/sdk-go/v2 v2.14.0/go.mod h1:xDmKfzNjM8gBvjaF8ijFjM1VYOVUEeUfapHMUX1T5To= -github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cockroachdb/cockroach-go/v2 v2.3.5 h1:Khtm8K6fTTz/ZCWPzU9Ne3aOW9VyAnj4qIPCJgKtwK0= -github.com/cockroachdb/cockroach-go/v2 v2.3.5/go.mod h1:1wNJ45eSXW9AnOc3skntW9ZUZz6gxrQK3cOj3rK+BC8= -github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/carlmjohnson/requests v0.25.1 h1:17zNRLecxtAjhtdEIV+F+wrYfe+AGZUjWJtpndcOUYA= +github.com/carlmjohnson/requests v0.25.1/go.mod h1:z3UEf8IE4sZxZ78spW6/tLdqBkfCu1Fn4RaYMnZ8SRM= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8= +github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0= +github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk= +github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= +github.com/cloudevents/sdk-go/v2 v2.16.2 h1:ZYDFrYke4FD+jM8TZTJJO6JhKHzOQl2oqpFK1D+NnQM= +github.com/cloudevents/sdk-go/v2 v2.16.2/go.mod h1:laOcGImm4nVJEU+PHnUrKL56CKmRL65RlQF0kRmW/kg= +github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik= +github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4= +github.com/cockroachdb/cockroach-go/v2 v2.4.3 h1:LJO3K3jC5WXvMePRQSJE1NsIGoFGcEx1LW83W6RAlhw= +github.com/cockroachdb/cockroach-go/v2 v2.4.3/go.mod h1:9U179XbCx4qFWtNhc7BiWLPfuyMVQ7qdAhfrwLz1vH0= +github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g= +github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8= -github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczCTSixgIKmwPv6+wP5DGjqLYw5SUiA= -github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgraph-io/ristretto/v2 v2.4.0 h1:I/w09yLjhdcVD2QV192UJcq8dPBaAJb9pOuMyNy0XlU= +github.com/dgraph-io/ristretto/v2 v2.4.0/go.mod h1:0KsrXtXvnv0EqnzyowllbVJB8yBonswa2lTCK2gGo9E= +github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da h1:aIftn67I1fkbMa512G+w+Pxci9hJPB8oMnkcP3iZF38= +github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM= -github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/go-gomail/gomail v0.0.0-20160411212932-81ebce5c23df/go.mod h1:GJr+FCSXshIwgHBtLglIg9M2l2kQSi6QjVAngtzI08Y= +github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA= +github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU= +github.com/envoyproxy/go-control-plane/envoy v1.37.0 h1:u3riX6BoYRfF4Dr7dwSOroNfdSbEPe9Yyl09/B6wBrQ= +github.com/envoyproxy/go-control-plane/envoy v1.37.0/go.mod h1:DReE9MMrmecPy+YvQOAOHNYMALuowAnbjjEMkkWOi6A= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0 h1:/G9QYbddjL25KvtKTv3an9lx6VBE2cnb8wp1vEGNYGI= +github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= +github.com/envoyproxy/protoc-gen-validate v1.3.3 h1:MVQghNeW+LZcmXe7SY1V36Z+WFMDjpqGAGacLe2T0ds= +github.com/envoyproxy/protoc-gen-validate v1.3.3/go.mod h1:TsndJ/ngyIdQRhMcVVGDDHINPLWB7C82oDArY51KfB0= +github.com/fatih/color v1.19.0 h1:Zp3PiM21/9Ld6FzSKyL5c/BULoe/ONr9KlbYVOfG8+w= +github.com/fatih/color v1.19.0/go.mod h1:zNk67I0ZUT1bEGsSGyCZYZNrHuTkJJB+r6Q9VuMi0LE= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/go-faster/city v1.0.1 h1:4WAxSZ3V2Ws4QRDrscLEDcibJY8uf41H6AhXDrNDcGw= +github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw= +github.com/go-faster/errors v0.7.1 h1:MkJTnDoEdi9pDabt1dpWf7AA8/BaSYZqibYyhZ20AYg= +github.com/go-faster/errors v0.7.1/go.mod h1:5ySTjWFiphBs07IKuiL69nxdfd5+fzh1u7FPGZP2quo= +github.com/go-hermes/hermes/v2 v2.6.2 h1:RuGQlICVtIHixfxtYwN7hAoqGyGxr+D3kE42oE6emcw= +github.com/go-hermes/hermes/v2 v2.6.2/go.mod h1:RLVNk31/1KqF35vK3mAaQVuJvMH+K5//6OTGJk+j/80= +github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA= +github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= -github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= -github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= -github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= -github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= -github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= -github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= -github.com/go-openapi/spec v0.20.9 h1:xnlYNQAwKd2VQRRfwTEI0DcK+2cbuvI/0c7jx3gA8/8= -github.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= -github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= -github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= -github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= -github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA= +github.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0= +github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE= +github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw= +github.com/go-openapi/spec v0.22.4 h1:4pxGjipMKu0FzFiu/DPwN3CTBRlVM2yLf/YTWorYfDQ= +github.com/go-openapi/spec v0.22.4/go.mod h1:WQ6Ai0VPWMZgMT4XySjlRIE6GP1bGQOtEThn3gcWLtQ= +github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= +github.com/go-openapi/swag/conv v0.25.5 h1:wAXBYEXJjoKwE5+vc9YHhpQOFj2JYBMF2DUi+tGu97g= +github.com/go-openapi/swag/conv v0.25.5/go.mod h1:CuJ1eWvh1c4ORKx7unQnFGyvBbNlRKbnRyAvDvzWA4k= +github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo= +github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU= +github.com/go-openapi/swag/jsonutils v0.25.5 h1:XUZF8awQr75MXeC+/iaw5usY/iM7nXPDwdG3Jbl9vYo= +github.com/go-openapi/swag/jsonutils v0.25.5/go.mod h1:48FXUaz8YsDAA9s5AnaUvAmry1UcLcNVWUjY42XkrN4= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5 h1:SX6sE4FrGb4sEnnxbFL/25yZBb5Hcg1inLeErd86Y1U= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5/go.mod h1:/2KvOTrKWjVA5Xli3DZWdMCZDzz3uV/T7bXwrKWPquo= +github.com/go-openapi/swag/loading v0.25.5 h1:odQ/umlIZ1ZVRteI6ckSrvP6e2w9UTF5qgNdemJHjuU= +github.com/go-openapi/swag/loading v0.25.5/go.mod h1:I8A8RaaQ4DApxhPSWLNYWh9NvmX2YKMoB9nwvv6oW6g= +github.com/go-openapi/swag/stringutils v0.25.5 h1:NVkoDOA8YBgtAR/zvCx5rhJKtZF3IzXcDdwOsYzrB6M= +github.com/go-openapi/swag/stringutils v0.25.5/go.mod h1:PKK8EZdu4QJq8iezt17HM8RXnLAzY7gW0O1KKarrZII= +github.com/go-openapi/swag/typeutils v0.25.5 h1:EFJ+PCga2HfHGdo8s8VJXEVbeXRCYwzzr9u4rJk7L7E= +github.com/go-openapi/swag/typeutils v0.25.5/go.mod h1:itmFmScAYE1bSD8C4rS0W+0InZUBrB2xSPbWt6DLGuc= +github.com/go-openapi/swag/yamlutils v0.25.5 h1:kASCIS+oIeoc55j28T4o8KwlV2S4ZLPT6G0iq2SSbVQ= +github.com/go-openapi/swag/yamlutils v0.25.5/go.mod h1:Gek1/SjjfbYvM+Iq4QGwa/2lEXde9n2j4a3wI3pNuOQ= +github.com/go-openapi/testify/enable/yaml/v2 v2.4.0 h1:7SgOMTvJkM8yWrQlU8Jm18VeDPuAvB/xWrdxFJkoFag= +github.com/go-openapi/testify/enable/yaml/v2 v2.4.0/go.mod h1:14iV8jyyQlinc9StD7w1xVPW3CO3q1Gj04Jy//Kw4VM= +github.com/go-openapi/testify/v2 v2.4.0 h1:8nsPrHVCWkQ4p8h1EsRVymA2XABB4OT40gcvAu+voFM= +github.com/go-openapi/testify/v2 v2.4.0/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= +github.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo= +github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU= +github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU= +github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/gofiber/contrib/otelfiber v1.0.10 h1:Bu28Pi4pfYmGfIc/9+sNaBbFwTHGY/zpSIK5jBxuRtM= github.com/gofiber/contrib/otelfiber v1.0.10/go.mod h1:jN6AvS1HolDHTQHFURsV+7jSX96FpXYeKH6nmkq8AIw= -github.com/gofiber/fiber/v2 v2.50.0 h1:ia0JaB+uw3GpNSCR5nvC5dsaxXjRU5OEu36aytx+zGw= -github.com/gofiber/fiber/v2 v2.50.0/go.mod h1:21eytvay9Is7S6z+OgPi7c7n4++tnClWmhpimVHMimw= -github.com/gofiber/swagger v0.1.14 h1:o524wh4QaS4eKhUCpj7M0Qhn8hvtzcyxDsfZLXuQcRI= -github.com/gofiber/swagger v0.1.14/go.mod h1:DCk1fUPsj+P07CKaZttBbV1WzTZSQcSxfub8y9/BFr8= -github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= -github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= -github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= -github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= -github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= -github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/gofiber/fiber/v2 v2.52.12 h1:0LdToKclcPOj8PktUdIKo9BUohjjwfnQl42Dhw8/WUw= +github.com/gofiber/fiber/v2 v2.52.12/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw= +github.com/gofiber/swagger v1.1.1 h1:FZVhVQQ9s1ZKLHL/O0loLh49bYB5l1HEAgxDlcTtkRA= +github.com/gofiber/swagger v1.1.1/go.mod h1:vtvY/sQAMc/lGTUCg0lqmBL7Ht9O7uzChpbvJeJQINw= +github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= +github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= +github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY= +github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= -github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o= -github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= -github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs= -github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0= -github.com/googleapis/gax-go/v2 v2.12.0 h1:A+gCJKdRfqXkr+BIRGtZLibNXf0m1f9E4HG56etFpas= -github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qKpsEkdD5+I6QGU= -github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= +github.com/google/martian/v3 v3.3.3 h1:DIhPTQrbPkgs2yJYdXU/eNACCG5DVQjySNRNlflZ9Fc= +github.com/google/martian/v3 v3.3.3/go.mod h1:iEPrYcgCF7jA9OtScMFQyAlZZ4YXTKEtJ1E6RWzmBA0= +github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= +github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.14 h1:yh8ncqsbUY4shRD5dA6RlzjJaT4hi3kII+zYw8wmLb8= +github.com/googleapis/enterprise-certificate-proxy v0.3.14/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg= +github.com/googleapis/gax-go/v2 v2.21.0 h1:h45NjjzEO3faG9Lg/cFrBh2PgegVVgzqKzuZl/wMbiI= +github.com/googleapis/gax-go/v2 v2.21.0/go.mod h1:But/NJU6TnZsrLai/xBAQLLz+Hc7fHZJt/hsCz3Fih4= github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8= github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1 h1:6UKoz5ujsI55KNpsJH3UwCq3T8kKbZwNZBNPuTTje8U= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1/go.mod h1:YvJ2f6MplWDhfxiUC3KpyTy76kYUZA4W3pTv/wdKQ9Y= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI= -github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ= -github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M= -github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= +github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= +github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48= +github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= +github.com/hashicorp/go-version v1.9.0 h1:CeOIz6k+LoN3qX9Z0tyQrPtiB1DFYRPfCIBtaXPSCnA= +github.com/hashicorp/go-version v1.9.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hirosassa/zerodriver v0.1.4 h1:8bzamKUOHHq03aEk12qi/lnji2dM+IhFOe+RpKpIZFM= github.com/hirosassa/zerodriver v0.1.4/go.mod h1:hHOOAQvVGwBV1iVVYujM6vwOBBqQcBIFpJxCD9mJU7Y= -github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4= -github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU= -github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= -github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= +github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/inbucket/html2text v1.0.0 h1:N5kza++4uBBDJ2Z3KUnTRyPNoBcW+YfOgNiNmNB+sgs= +github.com/inbucket/html2text v1.0.0/go.mod h1:5TrhXQKGU+LXurODaSm55Y9eXoPBRnYiOz4x2XfUoJU= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.5.0 h1:NxstgwndsTRy7eq9/kqYc/BZh5w2hHJV86wjvO+1xPw= -github.com/jackc/pgx/v5 v5.5.0/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= -github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= -github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= -github.com/jaytaylor/html2text v0.0.0-20180606194806-57d518f124b0/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= -github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 h1:iCHtR9CQyktQ5+f3dMVZfwD2KWJUgm7M0gdL9NGr8KA= -github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.9.2 h1:3ZhOzMWnR4yJ+RW1XImIPsD1aNSz4T4fyP7zlQb56hw= +github.com/jackc/pgx/v5 v5.9.2/go.mod h1:mal1tBGAFfLHvZzaYh77YS/eC6IX9OWbRV1QIIM0Jn4= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jaswdr/faker/v2 v2.9.1 h1:J0Rjqb2/FquZnoZplzkGVL5LmhNkeIpvsSMoJKzn+8E= +github.com/jaswdr/faker/v2 v2.9.1/go.mod h1:jZq+qzNQr8/P+5fHd9t3txe2GNPnthrTfohtnJ7B+68= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= @@ -217,44 +227,28 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible h1:jdpOPRN1zP63Td1hDQbZW73xKmzDvZHzVdNYxhnTMDA= github.com/jordan-wright/email v4.0.1-0.20210109023952-943e75fe5223+incompatible/go.mod h1:1c7szIrayyPPB/987hsnvNzLushdWf4o/79s3P08L8A= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= -github.com/jszwec/csvutil v1.8.0 h1:G7vS2LGdpZZDH1HmHeNbxOaJ/ZnJlpwGFvOkTkJzzNk= -github.com/jszwec/csvutil v1.8.0/go.mod h1:/E4ONrmGkwmWsk9ae9jpXnv9QT8pLHEPcCirMFhxG9I= -github.com/klauspost/compress v1.16.3/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ixi4= -github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/jszwec/csvutil v1.10.0 h1:upMDUxhQKqZ5ZDCs/wy+8Kib8rZR8I8lOR34yJkdqhI= +github.com/jszwec/csvutil v1.10.0/go.mod h1:/E4ONrmGkwmWsk9ae9jpXnv9QT8pLHEPcCirMFhxG9I= +github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE= +github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ= +github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= -github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/matcornic/hermes/v2 v2.1.0 h1:9TDYFBPFv6mcXanaDmRDEp/RTWj0dTTi+LpFnnnfNWc= -github.com/matcornic/hermes/v2 v2.1.0/go.mod h1:2+ziJeoyRfaLiATIL8VZ7f9hpzH4oDHqTmn0bhrsgVI= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/lib/pq v1.12.2 h1:ajJNv84limnK3aPbDIhLtcjrUbqAw/5XNdkuI6KNe/Q= +github.com/lib/pq v1.12.2/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= +github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= +github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= -github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI= -github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE= +github.com/mattn/go-runewidth v0.0.22 h1:76lXsPn6FyHtTY+jt2fTTvsMUCZq1k0qwRsAMuxzKAk= +github.com/mattn/go-runewidth v0.0.22/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= +github.com/mattn/go-sqlite3 v1.14.39 h1:sIwSjlJGOaRJjw44/HXaeTblZMjseqr6OOio1tz/+JI= +github.com/mattn/go-sqlite3 v1.14.39/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= @@ -264,309 +258,281 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= -github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nyaruka/phonenumbers v1.1.9 h1:/7bJVqIWLb+5erm10aMlojaKhXoMM6JKmlWLNg5laYc= -github.com/nyaruka/phonenumbers v1.1.9/go.mod h1:DC7jZd321FqUe+qWSNcHi10tyIyGNXGcNbfkPvdp1Vs= -github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/nyaruka/phonenumbers v1.7.1 h1:k8FHBMLegwW2tEIhsurC5YJk5Dix++H1k6liu1LUruY= +github.com/nyaruka/phonenumbers v1.7.1/go.mod h1:fsKPJ70O9JetEA4ggnJadYTFWwtGPvu/lETTXNXq6Cs= +github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj4EljqMiZsIcE09mmF8XsD5AYOJc= +github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0= +github.com/olekukonko/errors v1.2.0 h1:10Zcn4GeV59t/EGqJc8fUjtFT/FuUh5bTMzZ1XwmCRo= +github.com/olekukonko/errors v1.2.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y= +github.com/olekukonko/ll v0.1.8 h1:ysHCJRGHYKzmBSdz9w5AySztx7lG8SQY+naTGYUbsz8= +github.com/olekukonko/ll v0.1.8/go.mod h1:RPRC6UcscfFZgjo1nulkfMH5IM0QAYim0LfnMvUuozw= +github.com/olekukonko/tablewriter v1.1.4 h1:ORUMI3dXbMnRlRggJX3+q7OzQFDdvgbN9nVWj1drm6I= +github.com/olekukonko/tablewriter v1.1.4/go.mod h1:+kedxuyTtgoZLwif3P1Em4hARJs+mVnzKxmsCL/C5RY= github.com/palantir/stacktrace v0.0.0-20161112013806-78658fd2d177 h1:nRlQD0u1871kaznCnn1EvYiMbum36v7hw1DLPEjds4o= github.com/palantir/stacktrace v0.0.0-20161112013806-78658fd2d177/go.mod h1:ao5zGxj8Z4x60IOVYZUbDSmt3R8Ddo080vEgPosHpak= -github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/paulmach/orb v0.13.0 h1:r7n7mQGGF+cj/CbcivEj9J3HGK+XR+yXnvzRdq9saIw= +github.com/paulmach/orb v0.13.0/go.mod h1:6scRWINywA2Jf05dcjOfLfxrUIMECvTSG2MVbRLxu/k= +github.com/pierrec/lz4/v4 v4.1.26 h1:GrpZw1gZttORinvzBdXPUXATeqlJjqUG/D87TKMnhjY= +github.com/pierrec/lz4/v4 v4.1.26/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 h1:EaDatTxkdHG+U3Bk4EUr+DZ7fOGwTfezUiUJMaIcaho= -github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5/go.mod h1:fyalQWdtzDBECAQFBJuQe5bzQ02jGd5Qcbgb97Flm7U= -github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb27yVE+gIAfeqp8LUCc= -github.com/redis/go-redis/extra/redisotel/v9 v9.0.5/go.mod h1:WZjPDy7VNzn77AAfnAfVjZNvfJTYfPetfZk5yoSTLaQ= -github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= -github.com/redis/go-redis/v9 v9.3.0 h1:RiVDjmig62jIWp7Kk4XVLs0hzV6pI3PyTnnL0cnn0u0= -github.com/redis/go-redis/v9 v9.3.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= -github.com/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM= -github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk= -github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= -github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM= -github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= -github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= -github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= -github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= -github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0= -github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE= -github.com/sendgrid/sendgrid-go v3.13.0+incompatible h1:HZrzc06/QfBGesY9o3n1lvBrRONA+57rbDRKet7plos= -github.com/sendgrid/sendgrid-go v3.13.0+incompatible/go.mod h1:QRQt+LX/NmgVEvmdRw0VT/QgUn499+iza2FnDca9fg8= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pusher/pusher-http-go/v5 v5.1.1 h1:ZLUGdLA8yXMvByafIkS47nvuXOHrYmlh4bsQvuZnYVQ= +github.com/pusher/pusher-http-go/v5 v5.1.1/go.mod h1:Ibji4SGoUDtOy7CVRhCiEpgy+n5Xv6hSL/QqYOhmWW8= +github.com/redis/go-redis/extra/rediscmd/v9 v9.18.0 h1:QY4nmPHLFAJjtT5O4OMUEOxP8WVaRNOFpcbmxT2NLZU= +github.com/redis/go-redis/extra/rediscmd/v9 v9.18.0/go.mod h1:WH8cY/0fT41Bsf341qzo8v4nx0GCE8FykAA23IVbVmo= +github.com/redis/go-redis/extra/redisotel/v9 v9.18.0 h1:2dKdoEYBJ0CZCLPiCdvvc7luz3DPwY6hKdzjL6m1eHE= +github.com/redis/go-redis/extra/redisotel/v9 v9.18.0/go.mod h1:WzkrVG9ro9BwCQD0eJOWn6AGL4Z1CleGflM45w1hu10= +github.com/redis/go-redis/v9 v9.18.0 h1:pMkxYPkEbMPwRdenAzUNyFNrDgHx9U+DrBabWNfSRQs= +github.com/redis/go-redis/v9 v9.18.0/go.mod h1:k3ufPphLU5YXwNTUcCRXGxUoF1fqxnhFQmscfkCoDA0= +github.com/richardlehane/mscfb v1.0.6 h1:eN3bvvZCp00bs7Zf52bxNwAx5lJDBK1tCuH19qq5aC8= +github.com/richardlehane/mscfb v1.0.6/go.mod h1:pe0+IUIc0AHh0+teNzBlJCtSyZdFOGgV4ZK9bsoV+Jo= +github.com/richardlehane/msoleps v1.0.6 h1:9BvkpjvD+iUBalUY4esMwv6uBkfOip/Lzvd93jvR9gg= +github.com/richardlehane/msoleps v1.0.6/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/rs/zerolog v1.35.0 h1:VD0ykx7HMiMJytqINBsKcbLS+BJ4WYjz+05us+LRTdI= +github.com/rs/zerolog v1.35.0/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw= +github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= +github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w= +github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g= +github.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY= +github.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= +github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= +github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf h1:pvbZ0lM0XWPBqUKqFU8cmavspvIl9nulOYwdy6IFRRo= github.com/ssor/bom v0.0.0-20170718123548-6386211fdfcf/go.mod h1:RJID2RhlZKId02nZ62WenDCkgHFerpIOmW0iT7GKmXM= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/swaggo/files/v2 v2.0.0 h1:hmAt8Dkynw7Ssz46F6pn8ok6YmGZqHSVLZ+HQM7i0kw= -github.com/swaggo/files/v2 v2.0.0/go.mod h1:24kk2Y9NYEJ5lHuCra6iVwkMjIekMCaFq/0JQj66kyM= -github.com/swaggo/swag v1.16.2 h1:28Pp+8DkQoV+HLzLx8RGJZXNGKbFqnuvSbAAtoxiY04= -github.com/swaggo/swag v1.16.2/go.mod h1:6YzXnDcpr0767iOejs318CwYkCQqyGer6BizOg03f+E= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/swaggo/files/v2 v2.0.2 h1:Bq4tgS/yxLB/3nwOMcul5oLEUKa877Ykgz3CJMVbQKU= +github.com/swaggo/files/v2 v2.0.2/go.mod h1:TVqetIzZsO9OhHX1Am9sRf9LdrFZqoK49N37KON/jr0= +github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI= +github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg= github.com/thedevsaddam/govalidator v1.9.10 h1:m3dLRbSZ5Hts3VUWYe+vxLMG+FdyQuWOjzTeQRiMCvU= github.com/thedevsaddam/govalidator v1.9.10/go.mod h1:Ilx8u7cg5g3LXbSS943cx5kczyNuUn7LH/cK5MYuE90= -github.com/tinylib/msgp v1.1.8/go.mod h1:qkpG+2ldGg4xRFmx+jfTvZPxfGFhi64BcnL9vkCm/Tw= -github.com/unrolled/render v1.0.3/go.mod h1:gN9T0NhL4Bfbwu8ann7Ry/TGHYfosul+J0obPf6NBdM= -github.com/uptrace/uptrace-go v1.20.2 h1:+wfqrWqDpVi5lyjyqsj5ylkoQkWVRd61tGFe3szGxoc= -github.com/uptrace/uptrace-go v1.20.2/go.mod h1:StKbo3wpWwl1pFM/Uj9HXKOHAkEqiQ38Y8OVGvmSLz4= -github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= +github.com/tiendc/go-deepcopy v1.7.2 h1:Ut2yYR7W9tWjTQitganoIue4UGxZwCcJy3orjrrIj44= +github.com/tiendc/go-deepcopy v1.7.2/go.mod h1:4bKjNC2r7boYOkD2IOuZpYjmlDdzjbpTRyCx+goBCJQ= +github.com/tursodatabase/libsql-client-go v0.0.0-20251219100830-236aa1ff8acc h1:lzi/5fg2EfinRlh3v//YyIhnc4tY7BTqazQGwb1ar+0= +github.com/tursodatabase/libsql-client-go v0.0.0-20251219100830-236aa1ff8acc/go.mod h1:08inkKyguB6CGGssc/JzhmQWwBgFQBgjlYFjxjRh7nU= +github.com/uptrace/uptrace-go v1.41.1 h1:EtWkkdOQqtuJMZyzeU0zT5VH6ppVY12yOouQK3VRccw= +github.com/uptrace/uptrace-go v1.41.1/go.mod h1:gdn1eRLG3KCtTyiw+L8tG+tb/wnpiyIfLfTH2qh/5Mw= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M= -github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= -github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= -github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= -github.com/vanng822/css v0.0.0-20190504095207-a21e860bcd04/go.mod h1:tcnB1voG49QhCrwq1W0w5hhGasvOg+VQp9i9H1rCM1w= +github.com/valyala/fasthttp v1.69.0 h1:fNLLESD2SooWeh2cidsuFtOcrEi4uB4m1mPrkJMZyVI= +github.com/valyala/fasthttp v1.69.0/go.mod h1:4wA4PfAraPlAsJ5jMSqCE2ug5tqUPwKXxVj8oNECGcw= github.com/vanng822/css v1.0.1 h1:10yiXc4e8NI8ldU6mSrWmSWMuyWgPr9DZ63RSlsgDw8= github.com/vanng822/css v1.0.1/go.mod h1:tcnB1voG49QhCrwq1W0w5hhGasvOg+VQp9i9H1rCM1w= -github.com/vanng822/go-premailer v0.0.0-20191214114701-be27abe028fe/go.mod h1:JTFJA/t820uFDoyPpErFQ3rb3amdZoPtxcKervG0OE4= -github.com/vanng822/go-premailer v1.20.2 h1:vKs4VdtfXDqL7IXC2pkiBObc1bXM9bYH3Wa+wYw2DnI= -github.com/vanng822/go-premailer v1.20.2/go.mod h1:RAxbRFp6M/B171gsKu8dsyq+Y5NGsUUvYfg+WQWusbE= -github.com/vanng822/r2router v0.0.0-20150523112421-1023140a4f30/go.mod h1:1BVq8p2jVr55Ost2PkZWDrG86PiJ/0lxqcXoAcGxvWU= -github.com/xuri/efp v0.0.0-20230802181842-ad255f2331ca/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= -github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53 h1:Chd9DkqERQQuHpXjR/HSV1jLZA6uaoiwwH3vSuF3IW0= -github.com/xuri/efp v0.0.0-20231025114914-d1ff6096ae53/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= -github.com/xuri/excelize/v2 v2.8.0 h1:Vd4Qy809fupgp1v7X+nCS/MioeQmYVVzi495UCTqB7U= -github.com/xuri/excelize/v2 v2.8.0/go.mod h1:6iA2edBTKxKbZAa7X5bDhcCg51xdOn1Ar5sfoXRGrQg= -github.com/xuri/nfp v0.0.0-20230819163627-dc951e3ffe1a/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= -github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05 h1:qhbILQo1K3mphbwKh1vNm4oGezE1eF9fQWmNiIpSfI4= -github.com/xuri/nfp v0.0.0-20230919160717-d98342af3f05/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= +github.com/vanng822/go-premailer v1.33.0 h1:nglIpKn/7e3kIAwYByiH5xpauFur7RwAucqyZ59hcic= +github.com/vanng822/go-premailer v1.33.0/go.mod h1:LGYI7ym6FQ7KcHN16LiQRF+tlan7qwhP1KEhpTINFpo= +github.com/xuri/efp v0.0.1 h1:fws5Rv3myXyYni8uwj2qKjVaRP30PdjeYe2Y6FDsCL8= +github.com/xuri/efp v0.0.1/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI= +github.com/xuri/excelize/v2 v2.10.1 h1:V62UlqopMqha3kOpnlHy2CcRVw1V8E63jFoWUmMzxN0= +github.com/xuri/excelize/v2 v2.10.1/go.mod h1:iG5tARpgaEeIhTqt3/fgXCGoBRt4hNXgCp3tfXKoOIc= +github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9 h1:+C0TIdyyYmzadGaL/HBLbf3WdLgC29pgyhTjAT/0nuE= +github.com/xuri/nfp v0.0.2-0.20250530014748-2ddeb826f9a9/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ= +github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU= +github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib v1.21.0 h1:GT/BGfRiYerpC3frdNgouSXA8grRVwUT9Sbatrwddq0= -go.opentelemetry.io/contrib v1.21.0/go.mod h1:usW9bPlrjHiJFbK0a6yK/M5wNHs3nLmtrT3vzhoD3co= -go.opentelemetry.io/contrib/instrumentation/runtime v0.46.0 h1:dRj4IGqk65IHPLsur40gajPeQXxWWjprjeNq6aMJorU= -go.opentelemetry.io/contrib/instrumentation/runtime v0.46.0/go.mod h1:LD/bFNptUlSeHOX/6FMaAvjfvralTgFd09/EaZtV8X4= -go.opentelemetry.io/contrib/propagators/b3 v1.17.0 h1:ImOVvHnku8jijXqkwCSyYKRDt2YrnGXD4BbhcpfbfJo= -go.opentelemetry.io/otel v1.20.0 h1:vsb/ggIY+hUjD/zCAQHpzTmndPqv/ml2ArbsbfBYTAc= -go.opentelemetry.io/otel v1.20.0/go.mod h1:oUIGj3D77RwJdM6PPZImDpSZGDvkD9fhesHny69JFrs= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.43.0 h1:tFUz2BE6ucxU9PuPCwzbfDeQjMznIySJ4/73a3FSPUs= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.43.0/go.mod h1:hbzqqcIxyywu6UQ5J1wb4ntla8nCwCfNBZnMo2Dgh48= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0 h1:DeFD0VgTZ+Cj6hxravYYZE2W4GlneVH81iAOPjZkzk8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.20.0/go.mod h1:GijYcYmNpX1KazD5JmWGsi4P7dDTTTnfv1UbGn84MnU= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0 h1:gvmNvqrPYovvyRmCSygkUDyL8lC5Tl845MLEwqpxhEU= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.20.0/go.mod h1:vNUq47TGFioo+ffTSnKNdob241vePmtNZnAODKapKd0= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.42.0 h1:4jJuoeOo9W6hZnz+r046fyoH5kykZPRvKfUXJVfMpB0= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.20.0 h1:4s9HxB4azeeQkhY0GE5wZlMj4/pz8tE5gx2OQpGUw58= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.20.0/go.mod h1:djVA3TUJ2fSdMX0JE5XxFBOaZzprElJoP7fD4vnV2SU= -go.opentelemetry.io/otel/metric v1.20.0 h1:ZlrO8Hu9+GAhnepmRGhSU7/VkpjrNowxRN9GyKR4wzA= -go.opentelemetry.io/otel/metric v1.20.0/go.mod h1:90DRw3nfK4D7Sm/75yQ00gTJxtkBxX+wu6YaNymbpVM= +github.com/yuin/goldmark v1.8.2 h1:kEGpgqJXdgbkhcOgBxkC0X0PmoPG1ZyoZ117rDVp4zE= +github.com/yuin/goldmark v1.8.2/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg= +github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= +github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib v1.42.0 h1:845qj52z2T/bLInfZmG8AdbTO7delSd6eGVVHcAikzw= +go.opentelemetry.io/contrib v1.42.0/go.mod h1:JYdNU7Pl/2ckKMGp8/G7zeyhEbtRmy9Q8bcrtv75Znk= +go.opentelemetry.io/contrib/detectors/gcp v1.42.0 h1:kpt2PEJuOuqYkPcktfJqWWDjTEd/FNgrxcniL7kQrXQ= +go.opentelemetry.io/contrib/detectors/gcp v1.42.0/go.mod h1:W9zQ439utxymRrXsUOzZbFX4JhLxXU4+ZnCt8GG7yA8= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 h1:yI1/OhfEPy7J9eoa6Sj051C7n5dvpj0QX8g4sRchg04= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0/go.mod h1:NoUCKYWK+3ecatC4HjkRktREheMeEtrXoQxrqYFeHSc= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg= +go.opentelemetry.io/contrib/instrumentation/runtime v0.67.0 h1:fM78cKITJ2r08cl+nw5i+hI9zWAu3iak8o1Os/ca2Ck= +go.opentelemetry.io/contrib/instrumentation/runtime v0.67.0/go.mod h1:ybmlzIqGcQzwt5lAfi8TpSnHo/CI3yv1Czodmm+OJa8= +go.opentelemetry.io/contrib/processors/minsev v0.15.0 h1:82auGK0+tBbWa3Zy8RoLegy6OL1OULFk50W4eO2rSXE= +go.opentelemetry.io/contrib/processors/minsev v0.15.0/go.mod h1:+mJGjwRqiPNYDU1hehhHeO6On5DBqSX8JXOqBnawT20= +go.opentelemetry.io/contrib/propagators/b3 v1.19.0 h1:ulz44cpm6V5oAeg5Aw9HyqGFMS6XM7untlMEhD7YzzA= +go.opentelemetry.io/contrib/propagators/b3 v1.19.0/go.mod h1:OzCmE2IVS+asTI+odXQstRGVfXQ4bXv9nMBRK0nNyqQ= +go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I= +go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0 h1:HIBTQ3VO5aupLKjC90JgMqpezVXwFuq6Ryjn0/izoag= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.19.0/go.mod h1:ji9vId85hMxqfvICA0Jt8JqEdrXaAkcpkI9HPXya0ro= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0 h1:w1K+pCJoPpQifuVpsKamUdn9U0zM3xUziVOqsGksUrY= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.43.0/go.mod h1:HBy4BjzgVE8139ieRI75oXm3EcDN+6GhD88JT1Kjvxg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0 h1:88Y4s2C8oTui1LGM6bTWkw0ICGcOLCAI5l6zsD1j20k= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.43.0/go.mod h1:Vl1/iaggsuRlrHf/hfPJPvVag77kKyvrLeD10kpMl+A= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0 h1:3iZJKlCZufyRzPzlQhUIWVmfltrXuGyfjREgGP3UUjc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.43.0/go.mod h1:/G+nUPfhq2e+qiXMGxMwumDrP5jtzU+mWN7/sjT2rak= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.42.0 h1:lSZHgNHfbmQTPfuTmWVkEu8J8qXaQwuV30pjCcAUvP8= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.42.0/go.mod h1:so9ounLcuoRDu033MW/E0AD4hhUjVqswrMF5FoZlBcw= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0 h1:mS47AX77OtFfKG4vtp+84kuGSFZHTyxtXIN269vChY0= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.43.0/go.mod h1:PJnsC41lAGncJlPUniSwM81gc80GkgWJWr3cu2nKEtU= +go.opentelemetry.io/otel/log v0.19.0 h1:KUZs/GOsw79TBBMfDWsXS+KZ4g2Ckzksd1ymzsIEbo4= +go.opentelemetry.io/otel/log v0.19.0/go.mod h1:5DQYeGmxVIr4n0/BcJvF4upsraHjg6vudJJpnkL6Ipk= +go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM= +go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY= go.opentelemetry.io/otel/oteltest v1.0.0-RC3 h1:MjaeegZTaX0Bv9uB9CrdVjOFM/8slRjReoWoV9xDCpY= -go.opentelemetry.io/otel/sdk v1.20.0 h1:5Jf6imeFZlZtKv9Qbo6qt2ZkmWtdWx/wzcCbNUlAWGM= -go.opentelemetry.io/otel/sdk v1.20.0/go.mod h1:rmkSx1cZCm/tn16iWDn1GQbLtsW/LvsdEEFzCSRM6V0= -go.opentelemetry.io/otel/sdk/metric v1.20.0 h1:5eD40l/H2CqdKmbSV7iht2KMK0faAIL2pVYzJOWobGk= -go.opentelemetry.io/otel/sdk/metric v1.20.0/go.mod h1:AGvpC+YF/jblITiafMTYgvRBUiwi9hZf0EYE2E5XlS8= -go.opentelemetry.io/otel/trace v1.20.0 h1:+yxVAPZPbQhbC3OfAkeIVTky6iTFpcr4SiY9om7mXSQ= -go.opentelemetry.io/otel/trace v1.20.0/go.mod h1:HJSK7F/hA5RlzpZ0zKDCHCDHm556LCDtKaAo6JmBFUU= -go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= -go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +go.opentelemetry.io/otel/oteltest v1.0.0-RC3/go.mod h1:xpzajI9JBRr7gX63nO6kAmImmYIAtuQblZ36Z+LfCjE= +go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg= +go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg= +go.opentelemetry.io/otel/sdk/log v0.19.0 h1:scYVLqT22D2gqXItnWiocLUKGH9yvkkeql5dBDiXyko= +go.opentelemetry.io/otel/sdk/log v0.19.0/go.mod h1:vFBowwXGLlW9AvpuF7bMgnNI95LiW10szrOdvzBHlAg= +go.opentelemetry.io/otel/sdk/log/logtest v0.19.0 h1:BEbF7ZBB6qQloV/Ub1+3NQoOUnVtcGkU3XX4Ws3GQfk= +go.opentelemetry.io/otel/sdk/log/logtest v0.19.0/go.mod h1:Lua81/3yM0wOmoHTokLj9y9ADeA02v1naRrVrkAZuKk= +go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw= +go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A= +go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A= +go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0= +go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g= +go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= -golang.org/x/crypto v0.0.0-20181029175232-7e6ffbd03851/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200709230013-948cd5f35899/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= -golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/image v0.11.0 h1:ds2RoQvBvYTiJkwpSFDwCcDFNX7DqjL2WsUgTNk0Ooo= -golang.org/x/image v0.11.0/go.mod h1:bglhjqbqVuEb9e9+eNR45Jfu7D+T4Qan+NhQk8Ck2P8= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4= +golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA= +golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 h1:jiDhWWeC7jfWqR9c/uplMOqJ0sbNlNWv0UkzE0vX1MA= +golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90/go.mod h1:xE1HEv6b+1SCZ5/uscMRjUBKtIxworgEcEi+/n9NQDQ= +golang.org/x/image v0.25.0 h1:Y6uW6rH1y5y/LK1J8BPWZtr6yZ7hrsy6hFrXjgsc2fQ= +golang.org/x/image v0.25.0/go.mod h1:tCAmOEGthTtkalusGp1g3xa2gke8J6c2N565dTyl9Rs= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= -golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI= +golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= -golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= -golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.14.0 h1:P0Vrf/2538nmC0H+pEQ3MNFRRnVR7RlqyVw+bvm26z0= -golang.org/x/oauth2 v0.14.0/go.mod h1:lAtNWgaWfL4cm7j2OV8TxGi9Qb7ECORx8DktCY74OwM= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0= +golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw= +golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs= +golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4= +golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190225065934-cc5685c2db12/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo= +golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/time v0.4.0 h1:Z81tqI5ddIoXDPvVQ7/7CC9TnLM7ubaFG2qXYd5BbYY= -golang.org/x/time v0.4.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U= +golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= -golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= -golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s= +golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -google.golang.org/api v0.150.0 h1:Z9k22qD289SZ8gCJrk4DrWXkNjtfvKAUo/l1ma8eBYE= -google.golang.org/api v0.150.0/go.mod h1:ccy+MJ6nrYFgE3WgRx/AMXOxOmU8Q4hSa+jjibzhxcg= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4= +gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E= +google.golang.org/api v0.274.0 h1:aYhycS5QQCwxHLwfEHRRLf9yNsfvp1JadKKWBE54RFA= +google.golang.org/api v0.274.0/go.mod h1:JbAt7mF+XVmWu6xNP8/+CTiGH30ofmCmk9nM8d8fHew= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ= -google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY= -google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo= -google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14= -google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= -google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/genproto v0.0.0-20260401024825-9d38bb4040a9 h1:w8JYjr7zHemS95YA5FFwk+fUv5tdQU4I8twN9bFdxVU= +google.golang.org/genproto v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:YCEC8W7HTtK7iBv+pI7g7hGAi7qdGB6bQXw3BIYAusM= +google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA= +google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= +google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM= +google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= -google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/stretchr/testify.v1 v1.2.2 h1:yhQC6Uy5CqibAIlk1wlusa/MJ3iAN49/BsR/dCCKz3M= +gopkg.in/stretchr/testify.v1 v1.2.2/go.mod h1:QI5V/q6UbPmuhtm10CaFZxED9NreB8PnFYN9JcR6TxU= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gorm.io/datatypes v1.2.0 h1:5YT+eokWdIxhJgWHdrb2zYUimyk0+TaFth+7a0ybzco= -gorm.io/datatypes v1.2.0/go.mod h1:o1dh0ZvjIjhH/bngTpypG6lVRJ5chTBxE09FH/71k04= -gorm.io/driver/mysql v1.5.2 h1:QC2HRskSE75wBuOxe0+iCkyJZ+RqpudsQtqkp+IMuXs= -gorm.io/driver/mysql v1.5.2/go.mod h1:pQLhh1Ut/WUAySdTHwBpBv6+JKcj+ua4ZFx1QQTBzb8= -gorm.io/driver/postgres v1.5.4 h1:Iyrp9Meh3GmbSuyIAGyjkN+n9K+GHX9b9MqsTL4EJCo= -gorm.io/driver/postgres v1.5.4/go.mod h1:Bgo89+h0CRcdA33Y6frlaHHVuTdOf87pmyzwW9C/BH0= -gorm.io/driver/sqlite v1.5.0 h1:zKYbzRCpBrT1bNijRnxLDJWPjVfImGEn0lSnUY5gZ+c= -gorm.io/driver/sqlserver v1.4.1 h1:t4r4r6Jam5E6ejqP7N82qAJIJAht27EGT41HyPfXRw0= -gorm.io/gorm v1.25.2-0.20230530020048-26663ab9bf55/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k= -gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls= -gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= -gorm.io/plugin/opentelemetry v0.1.4 h1:7p0ocWELjSSRI7NCKPW2mVe6h43YPini99sNJcbsTuc= -gorm.io/plugin/opentelemetry v0.1.4/go.mod h1:tndJHOdvPT0pyGhOb8E2209eXJCUxhC5UpKw7bGVWeI= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +gorm.io/driver/clickhouse v0.7.0 h1:BCrqvgONayvZRgtuA6hdya+eAW5P2QVagV3OlEp1vtA= +gorm.io/driver/clickhouse v0.7.0/go.mod h1:TmNo0wcVTsD4BBObiRnCahUgHJHjBIwuRejHwYt3JRs= +gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg= +gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo= +gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4= +gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo= +gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ= +gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8= +gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg= +gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= +gorm.io/plugin/opentelemetry v0.1.16 h1:Kypj2YYAliJqkIczDZDde6P6sFMhKSlG5IpngMFQGpc= +gorm.io/plugin/opentelemetry v0.1.16/go.mod h1:P3RmTeZXT+9n0F1ccUqR5uuTvEXDxF8k2UpO7mTIB2Y= diff --git a/api/main.go b/api/main.go index 81982dd6..b85c9d66 100644 --- a/api/main.go +++ b/api/main.go @@ -3,19 +3,21 @@ package main import ( "fmt" "os" + "strings" - _ "github.com/NdoleStudio/httpsms/docs" + "github.com/NdoleStudio/httpsms/docs" "github.com/NdoleStudio/httpsms/pkg/di" + _ "github.com/tursodatabase/libsql-client-go/libsql" ) // Version is injected at runtime var Version string -// @title HTTP SMS API +// @title httpSMS API Reference // @version 1.0 -// @description API to send SMS messages using android [SmsManager](https://developer.android.com/reference/android/telephony/SmsManager) via HTTP +// @description Use your Android phone to send and receive SMS messages via a simple programmable API with end-to-end encryption. // -// @contact.name HTTP SMS +// @contact.name support@httpsms.com // @contact.email support@httpsms.com // // @license.name AGPL-3.0 @@ -33,6 +35,13 @@ func main() { di.LoadEnv() } - container := di.NewContainer("http-sms", Version) + if host := strings.TrimSpace(os.Getenv("SWAGGER_HOST")); len(host) > 0 { + docs.SwaggerInfo.Host = host + } + if len(Version) > 0 { + docs.SwaggerInfo.Version = Version + } + + container := di.NewContainer(os.Getenv("GCP_PROJECT_ID"), Version) container.Logger().Info(container.App().Listen(fmt.Sprintf("%s:%s", os.Getenv("APP_HOST"), os.Getenv("APP_PORT"))).Error()) } diff --git a/api/pkg/cache/memory_cache.go b/api/pkg/cache/memory_cache.go new file mode 100644 index 00000000..2e32bcff --- /dev/null +++ b/api/pkg/cache/memory_cache.go @@ -0,0 +1,47 @@ +package cache + +import ( + "context" + "fmt" + "time" + + "github.com/NdoleStudio/httpsms/pkg/telemetry" + "github.com/palantir/stacktrace" + ttlCache "github.com/patrickmn/go-cache" +) + +// memoryCache is the Cache implementation in memory +type memoryCache struct { + tracer telemetry.Tracer + store *ttlCache.Cache +} + +// NewMemoryCache creates a new instance of memoryCache +func NewMemoryCache(tracer telemetry.Tracer, store *ttlCache.Cache) Cache { + return &memoryCache{ + tracer: tracer, + store: store, + } +} + +// Get an item from the redis cache +func (cache *memoryCache) Get(ctx context.Context, key string) (value string, err error) { + ctx, span := cache.tracer.Start(ctx) + defer span.End() + + response, ok := cache.store.Get(key) + if !ok { + return "", stacktrace.NewError(fmt.Sprintf("no item found in cache with key [%s]", key)) + } + + return response.(string), nil +} + +// Set an item in the redis cache +func (cache *memoryCache) Set(ctx context.Context, key string, value string, ttl time.Duration) error { + ctx, span := cache.tracer.Start(ctx) + defer span.End() + + cache.store.Set(key, value, ttl) + return nil +} diff --git a/api/pkg/cache/redis_cache.go b/api/pkg/cache/redis_cache.go index ad2398fa..18a6a887 100644 --- a/api/pkg/cache/redis_cache.go +++ b/api/pkg/cache/redis_cache.go @@ -2,6 +2,7 @@ package cache import ( "context" + "errors" "fmt" "time" @@ -10,27 +11,27 @@ import ( "github.com/redis/go-redis/v9" ) -// RedisCache is the Cache implementation in redis -type RedisCache struct { +// redisCache is the Cache implementation in redis +type redisCache struct { tracer telemetry.Tracer client *redis.Client } // NewRedisCache creates a new instance of RedisCache func NewRedisCache(tracer telemetry.Tracer, client *redis.Client) Cache { - return &RedisCache{ + return &redisCache{ tracer: tracer, client: client, } } // Get an item from the redis cache -func (cache *RedisCache) Get(ctx context.Context, key string) (value string, err error) { +func (cache *redisCache) Get(ctx context.Context, key string) (value string, err error) { ctx, span := cache.tracer.Start(ctx) defer span.End() response, err := cache.client.Get(ctx, key).Result() - if err == redis.Nil { + if errors.Is(err, redis.Nil) { return "", stacktrace.Propagate(err, fmt.Sprintf("no item found in redis with key [%s]", key)) } if err != nil { @@ -40,7 +41,7 @@ func (cache *RedisCache) Get(ctx context.Context, key string) (value string, err } // Set an item in the redis cache -func (cache *RedisCache) Set(ctx context.Context, key string, value string, ttl time.Duration) error { +func (cache *redisCache) Set(ctx context.Context, key string, value string, ttl time.Duration) error { ctx, span := cache.tracer.Start(ctx) defer span.End() diff --git a/api/pkg/di/config.go b/api/pkg/di/config.go index 4655abc2..586934f9 100644 --- a/api/pkg/di/config.go +++ b/api/pkg/di/config.go @@ -2,6 +2,7 @@ package di import ( "log" + "os" "github.com/joho/godotenv" ) @@ -13,3 +14,12 @@ func LoadEnv(filenames ...string) { log.Fatalf("Fatal: cannot load .env file: %v", err) } } + +func getEnvWithDefault(key, defaultValue string) string { + value := os.Getenv(key) + if value == "" { + return defaultValue + } + + return value +} diff --git a/api/pkg/di/container.go b/api/pkg/di/container.go index c65a89d7..33d27b01 100644 --- a/api/pkg/di/container.go +++ b/api/pkg/di/container.go @@ -7,21 +7,29 @@ import ( "net/http" "os" "strconv" + "strings" "time" + plunk "github.com/NdoleStudio/plunk-go" + "github.com/pusher/pusher-http-go/v5" + "gorm.io/driver/sqlite" + + "github.com/NdoleStudio/httpsms/docs" + otelMetric "go.opentelemetry.io/otel/metric" - "github.com/dgraph-io/ristretto" + "github.com/dgraph-io/ristretto/v2" "github.com/gofiber/contrib/otelfiber" "gorm.io/plugin/opentelemetry/tracing" "github.com/NdoleStudio/httpsms/pkg/discord" + "cloud.google.com/go/storage" mexporter "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric" cloudtrace "github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/trace" "github.com/NdoleStudio/httpsms/pkg/cache" - lemonsqueezy "github.com/NdoleStudio/lemonsqueezy-go" + "github.com/NdoleStudio/lemonsqueezy-go" "github.com/hashicorp/go-retryablehttp" "github.com/redis/go-redis/extra/redisotel/v9" "github.com/redis/go-redis/v9" @@ -61,6 +69,7 @@ import ( fiberLogger "github.com/gofiber/fiber/v2/middleware/logger" "github.com/gofiber/swagger" "github.com/palantir/stacktrace" + ttlCache "github.com/patrickmn/go-cache" "gorm.io/gorm" "github.com/NdoleStudio/httpsms/pkg/handlers" @@ -72,13 +81,14 @@ import ( // Container is used to resolve services at runtime type Container struct { - projectID string - db *gorm.DB - yugaByteDB *gorm.DB - version string - app *fiber.App - eventDispatcher *services.EventDispatcher - logger telemetry.Logger + projectID string + db *gorm.DB + dedicatedDB *gorm.DB + version string + app *fiber.App + eventDispatcher *services.EventDispatcher + logger telemetry.Logger + attachmentRepository repositories.AttachmentRepository } // NewLiteContainer creates a Container without any routes or listeners @@ -110,6 +120,7 @@ func NewContainer(projectID string, version string) (container *Container) { container.RegisterMessageListeners() container.RegisterMessageRoutes() + container.RegisterAttachmentRoutes() container.RegisterBulkMessageRoutes() container.RegisterMessageThreadRoutes() @@ -142,6 +153,12 @@ func NewContainer(projectID string, version string) (container *Container) { container.RegisterDiscordRoutes() container.RegisterDiscordListeners() + container.RegisterPhoneAPIKeyRoutes() + container.RegisterPhoneAPIKeyListeners() + + container.RegisterMarketingListeners() + container.RegisterWebsocketListeners() + // this has to be last since it registers the /* route container.RegisterSwaggerRoutes() @@ -158,14 +175,21 @@ func (container *Container) App() (app *fiber.App) { app = fiber.New() - if os.Getenv("APP_HTTP_LOGGER") == "true" { + if os.Getenv("USE_HTTP_LOGGER") == "true" { app.Use(fiberLogger.New()) } app.Use(otelfiber.Middleware()) - app.Use(cors.New()) + app.Use(cors.New( + cors.Config{ + AllowOrigins: getEnvWithDefault("CORS_ALLOW_ORIGINS", "*"), + AllowHeaders: getEnvWithDefault("CORS_ALLOW_HEADERS", "*"), + AllowMethods: getEnvWithDefault("CORS_ALLOW_METHODS", "GET,POST,PUT,DELETE,OPTIONS"), + AllowCredentials: false, + ExposeHeaders: getEnvWithDefault("CORS_EXPOSE_HEADERS", "*"), + }), + ) app.Use(middlewares.HTTPRequestLogger(container.Tracer(), container.Logger())) - app.Use(middlewares.BearerAuth(container.Logger(), container.Tracer(), container.FirebaseAuthClient())) app.Use(middlewares.APIKeyAuth(container.Logger(), container.Tracer(), container.UserRepository())) @@ -179,18 +203,18 @@ func (container *Container) BearerAPIKeyMiddleware() fiber.Handler { return middlewares.BearerAPIKeyAuth(container.Logger(), container.Tracer(), container.UserRepository()) } +// PhoneAPIKeyMiddleware creates a new instance of middlewares.BearerAPIKeyAuth +func (container *Container) PhoneAPIKeyMiddleware() fiber.Handler { + container.logger.Debug("creating middlewares.PhoneAPIKeyMiddleware") + return middlewares.PhoneAPIKeyAuth(container.Logger(), container.Tracer(), container.PhoneAPIKeyRepository()) +} + // AuthenticatedMiddleware creates a new instance of middlewares.Authenticated func (container *Container) AuthenticatedMiddleware() fiber.Handler { container.logger.Debug("creating middlewares.Authenticated") return middlewares.Authenticated(container.Tracer()) } -// AuthRouter creates router for authenticated requests -func (container *Container) AuthRouter() fiber.Router { - container.logger.Debug("creating authRouter") - return container.App().Group("v1").Use(container.AuthenticatedMiddleware()) -} - // Logger creates a new instance of telemetry.Logger func (container *Container) Logger(skipFrameCount ...int) telemetry.Logger { container.logger.Debug("creating telemetry.Logger") @@ -209,37 +233,49 @@ func (container *Container) GormLogger() gormLogger.Interface { ) } -// YugaByteDB creates an instance of gorm.DB if it has not been created already -func (container *Container) YugaByteDB() (db *gorm.DB) { +func (container *Container) connect(dsn string, config *gorm.Config) (db *gorm.DB, err error) { + if strings.HasPrefix(dsn, "libsql://") { + return gorm.Open(sqlite.New(sqlite.Config{ + DriverName: "libsql", + DSN: dsn, + }), config) + } + return gorm.Open(postgres.Open(dsn), config) +} + +// DedicatedDB creates an instance of gorm.DB if it has not been created already +func (container *Container) DedicatedDB() (db *gorm.DB) { container.logger.Debug(fmt.Sprintf("creating %T", db)) - if container.yugaByteDB != nil { - return container.yugaByteDB + if container.dedicatedDB != nil { + return container.dedicatedDB } - config := &gorm.Config{} + config := &gorm.Config{ + TranslateError: true, + } if isLocal() { config = &gorm.Config{Logger: container.GormLogger()} } - db, err := gorm.Open(postgres.Open(os.Getenv("DATABASE_URL_YUGABYTE")), config) + db, err := container.connect(os.Getenv("DATABASE_URL_DEDICATED"), config) if err != nil { container.logger.Fatal(err) } - if err = db.Use(tracing.NewPlugin()); err != nil { - container.logger.Fatal(stacktrace.Propagate(err, "cannot use GORM tracing plugin")) - } - - sql, err := db.DB() + sqlDB, err := db.DB() if err != nil { - container.logger.Fatal(err) + container.logger.Fatal(stacktrace.Propagate(err, "cannot get sql.DB from GORM")) } - sql.SetMaxOpenConns(7) - sql.SetMaxIdleConns(3) - sql.SetConnMaxLifetime(time.Minute * 10) + sqlDB.SetMaxOpenConns(1) + sqlDB.SetMaxIdleConns(0) + sqlDB.SetConnMaxLifetime(10 * time.Second) + + if err = db.Use(tracing.NewPlugin()); err != nil { + container.logger.Fatal(stacktrace.Propagate(err, "cannot use GORM tracing plugin")) + } - container.logger.Debug(fmt.Sprintf("Running migrations for yugabyte [%T]", db)) + container.logger.Debug(fmt.Sprintf("Running migrations for dedicated [%T]", db)) if err = db.AutoMigrate(&entities.Heartbeat{}); err != nil { container.logger.Fatal(stacktrace.Propagate(err, fmt.Sprintf("cannot migrate %T", &entities.Heartbeat{}))) } @@ -248,8 +284,33 @@ func (container *Container) YugaByteDB() (db *gorm.DB) { container.logger.Fatal(stacktrace.Propagate(err, fmt.Sprintf("cannot migrate %T", &entities.HeartbeatMonitor{}))) } - container.yugaByteDB = db - return container.yugaByteDB + container.dedicatedDB = db + return container.dedicatedDB +} + +// DBWithoutMigration creates an instance of gorm.DB if it has not been created already +func (container *Container) DBWithoutMigration() (db *gorm.DB) { + if container.db != nil { + return container.db + } + + container.logger.Debug(fmt.Sprintf("creating %T", db)) + + config := &gorm.Config{TranslateError: true} + if isLocal() { + config.Logger = container.GormLogger() + } + + db, err := gorm.Open(postgres.Open(os.Getenv("DATABASE_URL")), config) + if err != nil { + container.logger.Fatal(err) + } + container.db = db + + if err = db.Use(tracing.NewPlugin()); err != nil { + container.logger.Fatal(stacktrace.Propagate(err, "cannot use GORM tracing plugin")) + } + return container.db } // DB creates an instance of gorm.DB if it has not been created already @@ -275,8 +336,22 @@ func (container *Container) DB() (db *gorm.DB) { container.logger.Fatal(stacktrace.Propagate(err, "cannot use GORM tracing plugin")) } + if os.Getenv("DATABASE_MIGRATION_SKIP") != "" { + container.logger.Debug(fmt.Sprintf("skipping migrations for [%T]", db)) + return container.db + } + container.logger.Debug(fmt.Sprintf("Running migrations for %T", db)) + // This prevents a bug in the Gorm AutoMigrate where it tries to delete this no existent constraints + // This is only applicable to PROD on cockroachDB + if os.Getenv("DATABASE_MIGRATION_CONSTRAINT_FIX") == "1" { + db.Exec(` +ALTER TABLE users ADD CONSTRAINT IF NOT EXISTS uni_users_api_key CHECK (api_key IS NOT NULL); +ALTER TABLE phone_api_keys ADD CONSTRAINT IF NOT EXISTS uni_phone_api_keys_api_key CHECK (api_key IS NOT NULL); +ALTER TABLE discords ADD CONSTRAINT IF NOT EXISTS uni_discords_server_id CHECK (server_id IS NOT NULL);`) + } + if err = db.AutoMigrate(&entities.Message{}); err != nil { container.logger.Fatal(stacktrace.Propagate(err, fmt.Sprintf("cannot migrate %T", &entities.Message{}))) } @@ -313,13 +388,17 @@ func (container *Container) DB() (db *gorm.DB) { container.logger.Fatal(stacktrace.Propagate(err, fmt.Sprintf("cannot migrate %T", &entities.Integration3CX{}))) } + if err = db.AutoMigrate(&entities.PhoneAPIKey{}); err != nil { + container.logger.Fatal(stacktrace.Propagate(err, fmt.Sprintf("cannot migrate %T", &entities.PhoneAPIKey{}))) + } + return container.db } // FirebaseApp creates a new instance of firebase.App func (container *Container) FirebaseApp() (app *firebase.App) { container.logger.Debug(fmt.Sprintf("creating %T", app)) - app, err := firebase.NewApp(context.Background(), nil, option.WithCredentialsJSON(container.FirebaseCredentials())) + app, err := firebase.NewApp(context.Background(), nil, option.WithAuthCredentialsJSON(option.ServiceAccount, container.FirebaseCredentials())) if err != nil { msg := "cannot initialize firebase application" container.logger.Fatal(stacktrace.Propagate(err, msg)) @@ -327,6 +406,13 @@ func (container *Container) FirebaseApp() (app *firebase.App) { return app } +// InMemoryCache creates a new instance of the in memory cache.Cache +func (container *Container) InMemoryCache() cache.Cache { + container.logger.Debug("creating an in memory cache") + c := ttlCache.New(time.Hour, time.Hour*2) + return cache.NewMemoryCache(container.Tracer(), c) +} + // Cache creates a new instance of cache.Cache func (container *Container) Cache() cache.Cache { container.logger.Debug("creating cache.Cache") @@ -454,6 +540,19 @@ func (container *Container) MessageHandlerValidator() (validator *validators.Mes container.Logger(), container.Tracer(), container.PhoneService(), + container.TurnstileTokenValidator(), + container.Cache(), + ) +} + +// TurnstileTokenValidator creates a new instance of validators.TurnstileTokenValidator +func (container *Container) TurnstileTokenValidator() (validator *validators.TurnstileTokenValidator) { + container.logger.Debug(fmt.Sprintf("creating %T", validator)) + return validators.NewTurnstileTokenValidator( + container.Logger(), + container.Tracer(), + os.Getenv("CLOUDFLARE_TURNSTILE_SECRET_KEY"), + container.HTTPClient("turnstile"), ) } @@ -465,6 +564,7 @@ func (container *Container) BulkMessageHandlerValidator() (validator *validators container.Tracer(), container.PhoneService(), container.UserService(), + container.Cache(), ) } @@ -574,6 +674,7 @@ func (container *Container) UserHandlerValidator() (validator *validators.UserHa return validators.NewUserHandlerValidator( container.Logger(), container.Tracer(), + container.UserService(), ) } @@ -620,6 +721,17 @@ func (container *Container) MessageRepository() (repository repositories.Message ) } +// PhoneAPIKeyRepository creates a new instance of repositories.PhoneAPIKeyRepository +func (container *Container) PhoneAPIKeyRepository() (repository repositories.PhoneAPIKeyRepository) { + container.logger.Debug("creating GORM repositories.PhoneAPIKeyRepository") + return repositories.NewGormPhoneAPIKeyRepository( + container.Logger(), + container.Tracer(), + container.DB(), + container.UserRistrettoCache(), + ) +} + // Integration3CXRepository creates a new instance of repositories.Integration3CxRepository func (container *Container) Integration3CXRepository() (repository repositories.Integration3CxRepository) { container.logger.Debug("creating GORM repositories.Integration3CxRepository") @@ -637,6 +749,7 @@ func (container *Container) PhoneRepository() (repository repositories.PhoneRepo container.Logger(), container.Tracer(), container.DB(), + container.PhoneRistrettoCache(), ) } @@ -690,33 +803,13 @@ func (container *Container) MessageThreadRepository() (repository repositories.M ) } -// EventRepository creates a new instance of repositories.EventRepository -func (container *Container) EventRepository() (repository repositories.EventRepository) { - container.logger.Debug("creating GORM repositories.EventRepository") - return repositories.NewGormEventRepository( - container.Logger(), - container.Tracer(), - container.DB(), - ) -} - // HeartbeatMonitorRepository creates a new instance of repositories.HeartbeatMonitorRepository func (container *Container) HeartbeatMonitorRepository() (repository repositories.HeartbeatMonitorRepository) { container.logger.Debug("creating GORM repositories.HeartbeatMonitorRepository") return repositories.NewGormHeartbeatMonitorRepository( container.Logger(), container.Tracer(), - container.YugaByteDB(), - ) -} - -// EventListenerLogRepository creates a new instance of repositories.EventListenerLogRepository -func (container *Container) EventListenerLogRepository() (repository repositories.EventListenerLogRepository) { - container.logger.Debug("creating GORM repositories.EventListenerLogRepository") - return repositories.NewGormEventListenerLogRepository( - container.Logger(), - container.Tracer(), - container.DB(), + container.DedicatedDB(), ) } @@ -738,7 +831,7 @@ func (container *Container) BillingService() (service *services.BillingService) return services.NewBillingService( container.Logger(), container.Tracer(), - container.Cache(), + container.InMemoryCache(), container.Mailer(), container.UserEmailFactory(), container.BillingUsageRepository(), @@ -764,7 +857,10 @@ func (container *Container) WebhookService() (service *services.WebhookService) return services.NewWebhookService( container.Logger(), container.Tracer(), - container.HTTPClient("webhook"), + &http.Client{ + Timeout: 6 * time.Second, + Transport: container.HTTPRoundTripperWithoutRetry("webhook"), + }, container.WebhookRepository(), container.EventDispatcher(), ) @@ -801,6 +897,16 @@ func (container *Container) HTTPRoundTripper(name string) http.RoundTripper { ) } +// HTTPRoundTripperWithoutRetry creates an open telemetry http.RoundTripper without retry +func (container *Container) HTTPRoundTripperWithoutRetry(name string) http.RoundTripper { + container.logger.Debug(fmt.Sprintf("Debug: initializing %s %T", name, http.DefaultTransport)) + return otelroundtripper.New( + otelroundtripper.WithName(name), + otelroundtripper.WithMeter(otel.GetMeterProvider().Meter(container.projectID)), + otelroundtripper.WithAttributes(container.OtelResources(container.version, container.projectID).Attributes()...), + ) +} + // OtelResources generates default open telemetry resources func (container *Container) OtelResources(version string, namespace string) *resource.Resource { return resource.NewWithAttributes( @@ -817,6 +923,7 @@ func (container *Container) RetryHTTPRoundTripper() http.RoundTripper { container.logger.Debug(fmt.Sprintf("initializing retry %T", http.DefaultTransport)) retryClient := retryablehttp.NewClient() retryClient.Logger = container.Logger() + retryClient.RetryMax = 2 return retryClient.StandardClient().Transport } @@ -838,8 +945,7 @@ func (container *Container) MarketingService() (service *services.MarketingServi container.Logger(), container.Tracer(), container.FirebaseAuthClient(), - os.Getenv("SENDGRID_API_KEY"), - os.Getenv("SENDGRID_LIST_ID"), + container.PlunkClient(), ) } @@ -852,8 +958,10 @@ func (container *Container) UserService() (service *services.UserService) { container.UserRepository(), container.Mailer(), container.UserEmailFactory(), - container.MarketingService(), container.LemonsqueezyClient(), + container.EventDispatcher(), + container.FirebaseAuthClient(), + container.HTTPClient("lemonsqueezy"), ) } @@ -900,6 +1008,7 @@ func (container *Container) MessageThreadService() (service *services.MessageThr container.Logger(), container.Tracer(), container.MessageThreadRepository(), + container.EventDispatcher(), ) } @@ -1023,6 +1132,18 @@ func (container *Container) Integration3CXHandler() (handler *handlers.Integrati ) } +// PhoneAPIKeyHandler creates a new instance of handlers.PhoneAPIKeyHandler +func (container *Container) PhoneAPIKeyHandler() (handler *handlers.PhoneAPIKeyHandler) { + container.logger.Debug(fmt.Sprintf("creating %T", handler)) + + return handlers.NewPhoneAPIKeyHandler( + container.Logger(), + container.Tracer(), + container.PhoneAPIKeyHandlerValidator(), + container.PhoneAPIKeyService(), + ) +} + // DiscordHandler creates a new instance of handlers.DiscordHandler func (container *Container) DiscordHandler() (handler *handlers.DiscordHandler) { container.logger.Debug(fmt.Sprintf("creating %T", handler)) @@ -1048,6 +1169,15 @@ func (container *Container) LemonsqueezyHandlerValidator() (validator *validator ) } +// PhoneAPIKeyHandlerValidator creates a new instance of validators.PhoneAPIKeyHandlerValidator +func (container *Container) PhoneAPIKeyHandlerValidator() (validator *validators.PhoneAPIKeyHandlerValidator) { + container.logger.Debug(fmt.Sprintf("creating %T", validator)) + return validators.NewPhoneAPIKeyHandlerValidator( + container.Logger(), + container.Tracer(), + ) +} + // LemonsqueezyClient creates a new instance of lemonsqueezy.Client func (container *Container) LemonsqueezyClient() (client *lemonsqueezy.Client) { container.logger.Debug(fmt.Sprintf("creating %T", client)) @@ -1058,6 +1188,18 @@ func (container *Container) LemonsqueezyClient() (client *lemonsqueezy.Client) { ) } +// PusherClient creates a new instance of pusher.Client +func (container *Container) PusherClient() (client *pusher.Client) { + container.logger.Debug(fmt.Sprintf("creating %T", client)) + return &pusher.Client{ + AppID: os.Getenv("PUSHER_APP_ID"), + Key: os.Getenv("PUSHER_KEY"), + Secret: os.Getenv("PUSHER_SECRET"), + Cluster: os.Getenv("PUSHER_CLUSTER"), + Secure: true, + } +} + // DiscordClient creates a new instance of discord.Client func (container *Container) DiscordClient() (client *discord.Client) { container.logger.Debug(fmt.Sprintf("creating %T", client)) @@ -1068,6 +1210,16 @@ func (container *Container) DiscordClient() (client *discord.Client) { ) } +// PlunkClient creates a new instance of plunk.Client +func (container *Container) PlunkClient() (client *plunk.Client) { + container.logger.Debug(fmt.Sprintf("creating %T", client)) + return plunk.New( + plunk.WithHTTPClient(container.HTTPClient("plunk")), + plunk.WithSecretKey(os.Getenv("PLUNK_SECRET_KEY")), + plunk.WithPublicKey(os.Getenv("PLUNK_PUBLIC_KEY")), + ) +} + // RegisterLemonsqueezyRoutes registers routes for the /lemonsqueezy prefix func (container *Container) RegisterLemonsqueezyRoutes() { container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.LemonsqueezyHandler{})) @@ -1080,6 +1232,12 @@ func (container *Container) RegisterIntegration3CXRoutes() { container.Integration3CXHandler().RegisterRoutes(container.App(), container.BearerAPIKeyMiddleware(), container.AuthenticatedMiddleware()) } +// RegisterPhoneAPIKeyRoutes registers routes for the /phone-api-key prefix +func (container *Container) RegisterPhoneAPIKeyRoutes() { + container.logger.Debug(fmt.Sprintf("registering [%T] routes", &handlers.Integration3CXHandler{})) + container.PhoneAPIKeyHandler().RegisterRoutes(container.App(), container.AuthenticatedMiddleware()) +} + // RegisterDiscordRoutes registers routes for the /discord prefix func (container *Container) RegisterDiscordRoutes() { container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.DiscordHandler{})) @@ -1184,6 +1342,26 @@ func (container *Container) RegisterDiscordListeners() { } } +// RegisterMarketingListeners registers event listeners for listeners.MarketingListener +func (container *Container) RegisterMarketingListeners() { + container.logger.Debug(fmt.Sprintf("registering listeners for %T", listeners.MarketingListener{})) + + if os.Getenv("PLUNK_SECRET_KEY") == "" { + container.logger.Debug("skipping marketing listeners because the PLUNK_SECRET_KEY env variable is not set") + return + } + + _, routes := listeners.NewMarketingListener( + container.Logger(), + container.Tracer(), + container.MarketingService(), + ) + + for event, handler := range routes { + container.EventDispatcher().Subscribe(event, handler) + } +} + // RegisterIntegration3CXListeners registers event listeners for listeners.Integration3CXListener func (container *Container) RegisterIntegration3CXListeners() { container.logger.Debug(fmt.Sprintf("registering listeners for %T", listeners.Integration3CXListener{})) @@ -1198,6 +1376,40 @@ func (container *Container) RegisterIntegration3CXListeners() { } } +// RegisterPhoneAPIKeyListeners registers event listeners for listeners.PhoneAPIKeyListener +func (container *Container) RegisterPhoneAPIKeyListeners() { + container.logger.Debug(fmt.Sprintf("registering listeners for %T", listeners.PhoneAPIKeyListener{})) + _, routes := listeners.NewPhoneAPIKeyListener( + container.Logger(), + container.Tracer(), + container.PhoneAPIKeyService(), + ) + + for event, handler := range routes { + container.EventDispatcher().Subscribe(event, handler) + } +} + +// RegisterWebsocketListeners registers event listeners for listeners.WebsocketListener +func (container *Container) RegisterWebsocketListeners() { + container.logger.Debug(fmt.Sprintf("registering listeners for %T", listeners.WebsocketListener{})) + + if os.Getenv("PUSHER_SECRET") == "" { + container.logger.Warn(stacktrace.NewError("skipping websocket listeners because the PUSHER_SECRET env variable is not set")) + return + } + + _, routes := listeners.NewWebsocketListener( + container.Logger(), + container.Tracer(), + container.PusherClient(), + ) + + for event, handler := range routes { + container.EventDispatcher().Subscribe(event, handler) + } +} + // RegisterWebhookListeners registers event listeners for listeners.WebhookListener func (container *Container) RegisterWebhookListeners() { container.logger.Debug(fmt.Sprintf("registering listeners for %T", listeners.WebhookListener{})) @@ -1221,6 +1433,71 @@ func (container *Container) MessageService() (service *services.MessageService) container.MessageRepository(), container.EventDispatcher(), container.PhoneService(), + container.AttachmentRepository(), + container.APIBaseURL(), + ) +} + +// AttachmentRepository creates a cached AttachmentRepository based on configuration +func (container *Container) AttachmentRepository() repositories.AttachmentRepository { + if container.attachmentRepository != nil { + return container.attachmentRepository + } + + bucket := os.Getenv("GCS_BUCKET_NAME") + if bucket != "" { + container.logger.Debug("creating GoogleCloudStorageAttachmentRepository") + client, err := storage.NewClient(context.Background(), option.WithAuthCredentialsJSON(option.ServiceAccount, container.FirebaseCredentials())) + if err != nil { + container.logger.Fatal(stacktrace.Propagate(err, "cannot create GCS client")) + } + container.attachmentRepository = repositories.NewGoogleCloudStorageAttachmentRepository( + container.Logger(), + container.Tracer(), + client, + bucket, + ) + } else { + container.logger.Debug("creating MemoryAttachmentRepository (GCS_BUCKET_NAME not set)") + container.attachmentRepository = repositories.NewMemoryAttachmentRepository( + container.Logger(), + container.Tracer(), + ) + } + + return container.attachmentRepository +} + +// APIBaseURL returns the API base URL derived from EVENTS_QUEUE_ENDPOINT +func (container *Container) APIBaseURL() string { + endpoint := os.Getenv("EVENTS_QUEUE_ENDPOINT") + return strings.TrimSuffix(endpoint, "/v1/events") +} + +// AttachmentHandler creates a new AttachmentHandler +func (container *Container) AttachmentHandler() (handler *handlers.AttachmentHandler) { + container.logger.Debug(fmt.Sprintf("creating %T", handler)) + return handlers.NewAttachmentHandler( + container.Logger(), + container.Tracer(), + container.AttachmentRepository(), + ) +} + +// RegisterAttachmentRoutes registers routes for the /attachments prefix +func (container *Container) RegisterAttachmentRoutes() { + container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.AttachmentHandler{})) + container.AttachmentHandler().RegisterRoutes(container.App()) +} + +// PhoneAPIKeyService creates a new instance of services.PhoneAPIKeyService +func (container *Container) PhoneAPIKeyService() (service *services.PhoneAPIKeyService) { + container.logger.Debug(fmt.Sprintf("creating %T", service)) + return services.NewPhoneAPIKeyService( + container.Logger(), + container.Tracer(), + container.PhoneRepository(), + container.PhoneAPIKeyRepository(), ) } @@ -1240,31 +1517,33 @@ func (container *Container) NotificationService() (service *services.PhoneNotifi // RegisterMessageRoutes registers routes for the /messages prefix func (container *Container) RegisterMessageRoutes() { container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.MessageHandler{})) - container.MessageHandler().RegisterRoutes(container.AuthRouter()) + container.MessageHandler().RegisterPhoneAPIKeyRoutes(container.App(), container.PhoneAPIKeyMiddleware(), container.AuthenticatedMiddleware()) + container.MessageHandler().RegisterRoutes(container.App(), container.AuthenticatedMiddleware()) } // RegisterBulkMessageRoutes registers routes for the /bulk-messages prefix func (container *Container) RegisterBulkMessageRoutes() { container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.BulkMessageHandler{})) - container.BulkMessageHandler().RegisterRoutes(container.AuthRouter()) + container.BulkMessageHandler().RegisterRoutes(container.App(), container.AuthenticatedMiddleware()) } // RegisterMessageThreadRoutes registers routes for the /message-threads prefix func (container *Container) RegisterMessageThreadRoutes() { container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.MessageThreadHandler{})) - container.MessageThreadHandler().RegisterRoutes(container.AuthRouter()) + container.MessageThreadHandler().RegisterRoutes(container.App(), container.AuthenticatedMiddleware()) } // RegisterHeartbeatRoutes registers routes for the /heartbeats prefix func (container *Container) RegisterHeartbeatRoutes() { container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.HeartbeatHandler{})) - container.HeartbeatHandler().RegisterRoutes(container.AuthRouter()) + container.HeartbeatHandler().RegisterRoutes(container.App(), container.AuthenticatedMiddleware()) + container.HeartbeatHandler().RegisterPhoneAPIKeyRoutes(container.App(), container.PhoneAPIKeyMiddleware(), container.AuthenticatedMiddleware()) } // RegisterBillingRoutes registers routes for the /billing prefix func (container *Container) RegisterBillingRoutes() { container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.BillingHandler{})) - container.BillingHandler().RegisterRoutes(container.AuthRouter()) + container.BillingHandler().RegisterRoutes(container.App(), container.AuthenticatedMiddleware()) } // RegisterWebhookRoutes registers routes for the /webhooks prefix @@ -1276,25 +1555,36 @@ func (container *Container) RegisterWebhookRoutes() { // RegisterPhoneRoutes registers routes for the /phone prefix func (container *Container) RegisterPhoneRoutes() { container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.PhoneHandler{})) - container.PhoneHandler().RegisterRoutes(container.AuthRouter()) + container.PhoneHandler().RegisterRoutes(container.App(), container.AuthenticatedMiddleware()) + container.PhoneHandler().RegisterPhoneAPIKeyRoutes(container.App(), container.PhoneAPIKeyMiddleware(), container.AuthenticatedMiddleware()) } // RegisterUserRoutes registers routes for the /users prefix func (container *Container) RegisterUserRoutes() { container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.UserHandler{})) - container.UserHandler().RegisterRoutes(container.AuthRouter()) + container.UserHandler().RegisterRoutes(container.App(), container.AuthenticatedMiddleware()) } // RegisterEventRoutes registers routes for the /events prefix func (container *Container) RegisterEventRoutes() { container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.EventsHandler{})) - container.EventsHandler().RegisterRoutes(container.AuthRouter()) + container.EventsHandler().RegisterRoutes(container.App(), container.AuthenticatedMiddleware()) } // RegisterSwaggerRoutes registers routes for swagger func (container *Container) RegisterSwaggerRoutes() { container.logger.Debug(fmt.Sprintf("registering %T routes", swagger.HandlerDefault)) - container.App().Get("/*", swagger.HandlerDefault) + container.App().Get("/*", swagger.New(swagger.Config{ + Title: docs.SwaggerInfo.Title, + CustomScript: ` + document.addEventListener("DOMContentLoaded", function(event) { + document.body.style.margin = '0'; + var links = document.querySelectorAll("link[rel~='icon']"); + links.forEach(function (link) { + link.href = 'https://httpsms.com/favicon.ico'; + }); + });`, + })) } // HeartbeatRepository registers a new instance of repositories.HeartbeatRepository @@ -1303,7 +1593,7 @@ func (container *Container) HeartbeatRepository() repositories.HeartbeatReposito return repositories.NewGormHeartbeatRepository( container.Logger(), container.Tracer(), - container.YugaByteDB(), + container.DedicatedDB(), ) } @@ -1313,21 +1603,35 @@ func (container *Container) UserRepository() repositories.UserRepository { return repositories.NewGormUserRepository( container.Logger(), container.Tracer(), - container.RistrettoCache(), + container.UserRistrettoCache(), container.DB(), ) } -// RistrettoCache creates an in-memory *ristretto.Cache -func (container *Container) RistrettoCache() (cache *ristretto.Cache) { +// PhoneRistrettoCache creates an in-memory *ristretto.Cache[string, *entities.Phone] +func (container *Container) PhoneRistrettoCache() (cache *ristretto.Cache[string, *entities.Phone]) { + container.logger.Debug(fmt.Sprintf("creating %T", cache)) + ristrettoCache, err := ristretto.NewCache[string, *entities.Phone](&ristretto.Config[string, *entities.Phone]{ + MaxCost: 5000, + NumCounters: 5000 * 10, + BufferItems: 64, + }) + if err != nil { + container.logger.Fatal(stacktrace.Propagate(err, "cannot create user ristretto cache")) + } + return ristrettoCache +} + +// UserRistrettoCache creates an in-memory *ristretto.Cache[string, entities.AuthContext] +func (container *Container) UserRistrettoCache() (cache *ristretto.Cache[string, entities.AuthContext]) { container.logger.Debug(fmt.Sprintf("creating %T", cache)) - ristrettoCache, err := ristretto.NewCache(&ristretto.Config{ + ristrettoCache, err := ristretto.NewCache[string, entities.AuthContext](&ristretto.Config[string, entities.AuthContext]{ MaxCost: 5000, NumCounters: 5000 * 10, BufferItems: 64, }) if err != nil { - container.logger.Fatal(stacktrace.Propagate(err, "cannot create ristretto cache")) + container.logger.Fatal(stacktrace.Propagate(err, "cannot create user ristretto cache")) } return ristrettoCache } @@ -1335,10 +1639,6 @@ func (container *Container) RistrettoCache() (cache *ristretto.Cache) { // InitializeTraceProvider initializes the open telemetry trace provider func (container *Container) InitializeTraceProvider() func() { return container.initializeUptraceProvider(container.version, container.projectID) - //if isLocal() { - // return container.initializeUptraceProvider(container.version, container.projectID) - //} - //return container.initializeGoogleTraceProvider(container.version, container.projectID) } func (container *Container) initializeGoogleTraceProvider(version string, namespace string) func() { diff --git a/api/pkg/emails/factory.go b/api/pkg/emails/factory.go index 4c7f1cf7..992f4864 100644 --- a/api/pkg/emails/factory.go +++ b/api/pkg/emails/factory.go @@ -14,6 +14,13 @@ func (factory *factory) formatPhoneNumber(number string) string { return phonenumbers.Format(value, phonenumbers.INTERNATIONAL) } +func (factory *factory) formatBool(value bool) string { + if value == true { + return "Yes" + } + return "No" +} + func (factory *factory) formatHTTPResponseCode(code *int) string { responseCode := "-" if code != nil { diff --git a/api/pkg/emails/hermes_mailer.go b/api/pkg/emails/hermes_mailer.go index c3adfd2f..7efe0f8a 100644 --- a/api/pkg/emails/hermes_mailer.go +++ b/api/pkg/emails/hermes_mailer.go @@ -5,7 +5,7 @@ import ( "strconv" "time" - "github.com/matcornic/hermes/v2" + "github.com/go-hermes/hermes/v2" ) // HermesGeneratorConfig contains details for the generator diff --git a/api/pkg/emails/hermes_notification_email_factory.go b/api/pkg/emails/hermes_notification_email_factory.go index c3e8b1b9..7c4b7bc5 100644 --- a/api/pkg/emails/hermes_notification_email_factory.go +++ b/api/pkg/emails/hermes_notification_email_factory.go @@ -6,10 +6,8 @@ import ( "github.com/NdoleStudio/httpsms/pkg/events" - "github.com/google/uuid" - "github.com/NdoleStudio/httpsms/pkg/entities" - "github.com/matcornic/hermes/v2" + "github.com/go-hermes/hermes/v2" "github.com/palantir/stacktrace" ) @@ -35,11 +33,11 @@ func (factory *hermesNotificationEmailFactory) DiscordSendFailed(user *entities. fmt.Sprintf("We ran into an error while fowarding an incoming SMS to your discord server at %s", user.UserTimeString(time.Now())), }, Dictionary: []hermes.Entry{ - {"Discord Channel ID", payload.DiscordChannelID}, - {"Event Name", payload.EventType}, - {"Phone Number", factory.formatPhoneNumber(payload.Owner)}, - {"HTTP Response Code", factory.formatHTTPResponseCode(payload.HTTPResponseStatusCode)}, - {"Error Message / HTTP Response", payload.ErrorMessage}, + {Key: "Discord Channel ID", Value: payload.DiscordChannelID}, + {Key: "Event Name", Value: payload.EventType}, + {Key: "Phone Number", Value: factory.formatPhoneNumber(payload.Owner)}, + {Key: "HTTP Response Code", Value: factory.formatHTTPResponseCode(payload.HTTPResponseStatusCode)}, + {Key: "Error Message / HTTP Response", Value: payload.ErrorMessage}, }, Actions: []hermes.Action{ { @@ -85,12 +83,13 @@ func (factory *hermesNotificationEmailFactory) WebhookSendFailed(user *entities. fmt.Sprintf("We ran into an error while fowarding a webhook event from httpSMS to your webserver at %s", user.UserTimeString(time.Now())), }, Dictionary: []hermes.Entry{ - {"Server URL", payload.WebhookURL}, - {"Event Name", payload.EventType}, - {"Event ID", payload.EventID}, - {"Phone Number", factory.formatPhoneNumber(payload.Owner)}, - {"HTTP Response Code", factory.formatHTTPResponseCode(payload.HTTPResponseStatusCode)}, - {"Error Message / HTTP Response", payload.ErrorMessage}, + {Key: "Server URL", Value: payload.WebhookURL}, + {Key: "Event Name", Value: payload.EventType}, + {Key: "Event ID", Value: payload.EventID}, + {Key: "Phone Number", Value: factory.formatPhoneNumber(payload.Owner)}, + {Key: "HTTP Response Code", Value: factory.formatHTTPResponseCode(payload.HTTPResponseStatusCode)}, + {Key: "Error Message / HTTP Response", Value: payload.ErrorMessage}, + {Key: "Event Payload", Value: payload.EventPayload}, }, Actions: []hermes.Action{ { @@ -128,18 +127,19 @@ func (factory *hermesNotificationEmailFactory) WebhookSendFailed(user *entities. }, nil } -func (factory *hermesNotificationEmailFactory) MessageExpired(user *entities.User, messageID uuid.UUID, owner string, contact string, content string) (*Email, error) { +func (factory *hermesNotificationEmailFactory) MessageExpired(user *entities.User, payload *events.MessageSendExpiredPayload) (*Email, error) { email := hermes.Email{ Body: hermes.Body{ Title: "Hello", Intros: []string{ - fmt.Sprintf("The SMS message which you sent to %s has expired at %s and you will need to resend this message.", factory.formatPhoneNumber(contact), user.UserTimeString(time.Now())), + fmt.Sprintf("The SMS message which you sent to %s has expired at %s and you will need to resend this message.", factory.formatPhoneNumber(payload.Contact), user.UserTimeString(time.Now())), }, Dictionary: []hermes.Entry{ - {"ID", messageID.String()}, - {"From", factory.formatPhoneNumber(owner)}, - {"To", factory.formatPhoneNumber(contact)}, - {"Message", content}, + {Key: "ID", Value: payload.MessageID.String()}, + {Key: "From", Value: factory.formatPhoneNumber(payload.Owner)}, + {Key: "To", Value: factory.formatPhoneNumber(payload.Contact)}, + {Key: "Message", Value: payload.Content}, + {Key: "Encrypted", Value: factory.formatBool(payload.Encrypted)}, }, Actions: []hermes.Action{ { @@ -177,19 +177,20 @@ func (factory *hermesNotificationEmailFactory) MessageExpired(user *entities.Use }, nil } -func (factory *hermesNotificationEmailFactory) MessageFailed(user *entities.User, messageID uuid.UUID, owner, contact, content, reason string) (*Email, error) { +func (factory *hermesNotificationEmailFactory) MessageFailed(user *entities.User, payload *events.MessageSendFailedPayload) (*Email, error) { email := hermes.Email{ Body: hermes.Body{ Title: "Hello", Intros: []string{ - fmt.Sprintf("The SMS message which you sent to %s has failed at %s and you will need to resend this message.", factory.formatPhoneNumber(contact), user.UserTimeString(time.Now())), + fmt.Sprintf("The SMS message which you sent to %s has failed at %s and you will need to resend this message.", factory.formatPhoneNumber(payload.Contact), user.UserTimeString(time.Now())), }, Dictionary: []hermes.Entry{ - {"ID", messageID.String()}, - {"From", factory.formatPhoneNumber(owner)}, - {"To", factory.formatPhoneNumber(contact)}, - {"Message", content}, - {"Failure Reason", reason}, + {Key: "ID", Value: payload.ID.String()}, + {Key: "From", Value: factory.formatPhoneNumber(payload.Owner)}, + {Key: "To", Value: factory.formatPhoneNumber(payload.Contact)}, + {Key: "Message", Value: payload.Content}, + {Key: "Encrypted", Value: factory.formatBool(payload.Encrypted)}, + {Key: "Failure Reason", Value: payload.ErrorMessage}, }, Actions: []hermes.Action{ { diff --git a/api/pkg/emails/hermes_theme.go b/api/pkg/emails/hermes_theme.go index c19b30c8..9d8cc471 100644 --- a/api/pkg/emails/hermes_theme.go +++ b/api/pkg/emails/hermes_theme.go @@ -1,10 +1,14 @@ package emails -import "github.com/matcornic/hermes/v2" +import "github.com/go-hermes/hermes/v2" // hermesTheme is the theme by default type hermesTheme struct{} +func (dt *hermesTheme) Styles() hermes.StylesDefinition { + return hermes.Default{}.Styles() +} + func newHermesTheme() hermes.Theme { return &hermesTheme{} } diff --git a/api/pkg/emails/hermes_user_email_factory.go b/api/pkg/emails/hermes_user_email_factory.go index 8a177f0b..9ec5754a 100644 --- a/api/pkg/emails/hermes_user_email_factory.go +++ b/api/pkg/emails/hermes_user_email_factory.go @@ -5,7 +5,7 @@ import ( "time" "github.com/NdoleStudio/httpsms/pkg/entities" - "github.com/matcornic/hermes/v2" + "github.com/go-hermes/hermes/v2" "github.com/palantir/stacktrace" ) @@ -15,6 +15,54 @@ type hermesUserEmailFactory struct { generator hermes.Hermes } +func (factory *hermesUserEmailFactory) APIKeyRotated(emailAddress string, timestamp time.Time, timezone string) (*Email, error) { + location, err := time.LoadLocation(timezone) + if err != nil { + location = time.UTC + } + + email := hermes.Email{ + Body: hermes.Body{ + Intros: []string{ + fmt.Sprintf("This is a confirmation email that your httpSMS API Key has been successfully rotated at %s.", timestamp.In(location).Format(time.RFC1123)), + }, + Actions: []hermes.Action{ + { + Instructions: "You can see your new API key in the httpSMS settings page.", + Button: hermes.Button{ + Color: "#329ef4", + TextColor: "#FFFFFF", + Text: "httpSMS Settings", + Link: "https://httpsms.com/settings/", + }, + }, + }, + Title: "Hey,", + Signature: "Cheers", + Outros: []string{ + fmt.Sprintf("If you did not trigger this API key rotation please contact us immediately by replying to this email."), + }, + }, + } + + html, err := factory.generator.GenerateHTML(email) + if err != nil { + return nil, stacktrace.Propagate(err, "cannot generate html email") + } + + text, err := factory.generator.GeneratePlainText(email) + if err != nil { + return nil, stacktrace.Propagate(err, "cannot generate text email") + } + + return &Email{ + ToEmail: emailAddress, + Subject: "Your httpSMS API Key has been rotated successfully", + HTML: html, + Text: text, + }, nil +} + // UsageLimitExceeded is the email sent when the plan limit is reached func (factory *hermesUserEmailFactory) UsageLimitExceeded(user *entities.User) (*Email, error) { email := hermes.Email{ diff --git a/api/pkg/emails/notification_email_factory.go b/api/pkg/emails/notification_email_factory.go index a25b90a8..72c8c16f 100644 --- a/api/pkg/emails/notification_email_factory.go +++ b/api/pkg/emails/notification_email_factory.go @@ -3,16 +3,15 @@ package emails import ( "github.com/NdoleStudio/httpsms/pkg/entities" "github.com/NdoleStudio/httpsms/pkg/events" - "github.com/google/uuid" ) // NotificationEmailFactory generates emails to users about a message type NotificationEmailFactory interface { // MessageExpired sends an email when the user's message is expired - MessageExpired(user *entities.User, messageID uuid.UUID, owner, contact, content string) (*Email, error) + MessageExpired(user *entities.User, payload *events.MessageSendExpiredPayload) (*Email, error) // MessageFailed sends an email when the user's message is failed - MessageFailed(user *entities.User, messageID uuid.UUID, owner, contact, content, reason string) (*Email, error) + MessageFailed(user *entities.User, payload *events.MessageSendFailedPayload) (*Email, error) // DiscordSendFailed sends an email when the user's discord message is failed DiscordSendFailed(user *entities.User, payload *events.DiscordSendFailedPayload) (*Email, error) diff --git a/api/pkg/emails/user_email_factory.go b/api/pkg/emails/user_email_factory.go index a26f42ec..a8c11d7a 100644 --- a/api/pkg/emails/user_email_factory.go +++ b/api/pkg/emails/user_email_factory.go @@ -16,4 +16,7 @@ type UserEmailFactory interface { // UsageLimitAlert sends an email when a user is approaching the limit UsageLimitAlert(user *entities.User, usage *entities.BillingUsage) (*Email, error) + + // APIKeyRotated sends an email when the API key is rotated + APIKeyRotated(email string, timestamp time.Time, timezone string) (*Email, error) } diff --git a/api/pkg/entities/auth_context.go b/api/pkg/entities/auth_context.go new file mode 100644 index 00000000..fd6db242 --- /dev/null +++ b/api/pkg/entities/auth_context.go @@ -0,0 +1,16 @@ +package entities + +import "github.com/google/uuid" + +// AuthContext is the user gotten from an auth request +type AuthContext struct { + ID UserID `json:"id"` + PhoneAPIKeyID *uuid.UUID `json:"phone_api_key_id"` + PhoneNumbers []string `json:"phone_numbers"` + Email string `json:"email"` +} + +// IsNoop checks if a user is empty +func (user AuthContext) IsNoop() bool { + return user.ID == "" || user.Email == "" +} diff --git a/api/pkg/entities/auth_user.go b/api/pkg/entities/auth_user.go deleted file mode 100644 index 494895bc..00000000 --- a/api/pkg/entities/auth_user.go +++ /dev/null @@ -1,12 +0,0 @@ -package entities - -// AuthUser is the user gotten from an auth request -type AuthUser struct { - ID UserID `json:"id"` - Email string `json:"email"` -} - -// IsNoop checks if a user is empty -func (user AuthUser) IsNoop() bool { - return user.ID == "" || user.Email == "" -} diff --git a/api/pkg/entities/discord.go b/api/pkg/entities/discord.go index 6cad93ed..13b2e903 100644 --- a/api/pkg/entities/discord.go +++ b/api/pkg/entities/discord.go @@ -11,7 +11,7 @@ type Discord struct { ID uuid.UUID `json:"id" gorm:"primaryKey;type:uuid;" example:"32343a19-da5e-4b1b-a767-3298a73703cb"` UserID UserID `json:"user_id" gorm:"index" example:"WB7DRDWrJZRGbYrv2CKGkqbzvqdC"` Name string `json:"name" example:"Game Server"` - ServerID string `json:"server_id" gorm:"uniqueIndex" example:"1095778291488653372"` + ServerID string `json:"server_id" gorm:"uniqueIndex:idx_discords_server_id;NOT NULL" example:"1095778291488653372"` IncomingChannelID string `json:"incoming_channel_id" example:"1095780203256627291"` CreatedAt time.Time `json:"created_at" example:"2022-06-05T14:26:02.302718+03:00"` UpdatedAt time.Time `json:"updated_at" example:"2022-06-05T14:26:10.303278+03:00"` diff --git a/api/pkg/entities/event_listener_log.go b/api/pkg/entities/event_listener_log.go deleted file mode 100644 index 50f0662c..00000000 --- a/api/pkg/entities/event_listener_log.go +++ /dev/null @@ -1,18 +0,0 @@ -package entities - -import ( - "time" - - "github.com/google/uuid" -) - -// EventListenerLog stores the log of all the events handled -type EventListenerLog struct { - ID uuid.UUID `json:"id" gorm:"primaryKey;type:uuid;"` - EventID string `json:"event_id" gorm:"index:idx_event_listener_log_event_id_handler"` - EventType string `json:"event_type"` - Handler string `json:"handler" gorm:"index:idx_event_listener_log_event_id_handler"` - Duration time.Duration `json:"duration"` - HandledAt time.Time `json:"handled_at"` - CreatedAt time.Time `json:"created_at"` -} diff --git a/api/pkg/entities/heartbeat_monitor.go b/api/pkg/entities/heartbeat_monitor.go index ef1e8e74..6b41a31a 100644 --- a/api/pkg/entities/heartbeat_monitor.go +++ b/api/pkg/entities/heartbeat_monitor.go @@ -8,16 +8,22 @@ import ( // HeartbeatMonitor is used to monitor heartbeats of a phone type HeartbeatMonitor struct { - ID uuid.UUID `json:"id" gorm:"primaryKey;type:uuid;" example:"32343a19-da5e-4b1b-a767-3298a73703cb"` - PhoneID uuid.UUID `json:"phone_id" example:"32343a19-da5e-4b1b-a767-3298a73703cb"` - UserID UserID `json:"user_id" example:"WB7DRDWrJZRGbYrv2CKGkqbzvqdC"` - QueueID string `json:"queue_id" example:"0360259236613675274"` - Owner string `json:"owner" example:"+18005550199"` - CreatedAt time.Time `json:"created_at" example:"2022-06-05T14:26:02.302718+03:00"` - UpdatedAt time.Time `json:"updated_at" example:"2022-06-05T14:26:10.303278+03:00"` + ID uuid.UUID `json:"id" gorm:"primaryKey;type:uuid;" example:"32343a19-da5e-4b1b-a767-3298a73703cb"` + PhoneID uuid.UUID `json:"phone_id" example:"32343a19-da5e-4b1b-a767-3298a73703cb"` + UserID UserID `json:"user_id" example:"WB7DRDWrJZRGbYrv2CKGkqbzvqdC"` + QueueID string `json:"queue_id" example:"0360259236613675274"` + Owner string `json:"owner" example:"+18005550199"` + PhoneOnline bool `json:"phone_online" example:"true" default:"true"` + CreatedAt time.Time `json:"created_at" example:"2022-06-05T14:26:02.302718+03:00"` + UpdatedAt time.Time `json:"updated_at" example:"2022-06-05T14:26:10.303278+03:00"` } // RequiresCheck returns true if the heartbeat monitor requires a check func (h *HeartbeatMonitor) RequiresCheck() bool { return h.UpdatedAt.Add(2 * time.Hour).Before(time.Now()) } + +// PhoneIsOffline returns true if the phone is offline +func (h *HeartbeatMonitor) PhoneIsOffline() bool { + return !h.PhoneOnline +} diff --git a/api/pkg/entities/message.go b/api/pkg/entities/message.go index d5fffaa8..52a9a221 100644 --- a/api/pkg/entities/message.go +++ b/api/pkg/entities/message.go @@ -4,6 +4,7 @@ import ( "time" "github.com/google/uuid" + "github.com/lib/pq" ) // MessageType is the type of message if it is incoming or outgoing @@ -15,6 +16,9 @@ const ( // MessageTypeMobileOriginated means the message comes directly from a mobile phone MessageTypeMobileOriginated = "mobile-originated" + + // MessageTypeCallMissed means the message is generated when a phone call is missed by the android phone + MessageTypeCallMissed = "call/missed" ) // MessageStatus is the status of the message @@ -33,7 +37,7 @@ const ( // MessageStatusSent means the message has already sent by the mobile phone MessageStatusSent = "sent" - // MessageStatusReceived means the message was received by tne mobile phone (MO) + // MessageStatusReceived means the message was received by the mobile phone (MO) or a phone call is missed by the mobile phone MessageStatusReceived = "received" // MessageStatusFailed means the mobile phone could not send the message @@ -44,6 +48,9 @@ const ( // MessageStatusExpired means the message could not be sent by the mobile phone after 5 minutes MessageStatusExpired = "expired" + + // MessageStatusDeleted is for deleted messages and threads + MessageStatusDeleted = "deleted" ) // MessageEventName is the type of event generated by the mobile phone for a message @@ -77,14 +84,16 @@ func (s SIM) String() string { // Message represents a message sent between 2 phone numbers type Message struct { - ID uuid.UUID `json:"id" gorm:"primaryKey;type:uuid;" example:"32343a19-da5e-4b1b-a767-3298a73703cb"` - RequestID *string `json:"request_id" example:"153554b5-ae44-44a0-8f4f-7bbac5657ad4"` - Owner string `json:"owner" example:"+18005550199"` - UserID UserID `json:"user_id" gorm:"index:idx_messages__user_id" example:"WB7DRDWrJZRGbYrv2CKGkqbzvqdC"` - Contact string `json:"contact" example:"+18005550100"` - Content string `json:"content" example:"This is a sample text message"` - Type MessageType `json:"type" example:"mobile-terminated"` - Status MessageStatus `json:"status" example:"pending"` + ID uuid.UUID `json:"id" gorm:"primaryKey;type:uuid;" example:"32343a19-da5e-4b1b-a767-3298a73703cb"` + RequestID *string `json:"request_id" example:"153554b5-ae44-44a0-8f4f-7bbac5657ad4" validate:"optional"` + Owner string `json:"owner" example:"+18005550199"` + UserID UserID `json:"user_id" gorm:"index:idx_messages__user_id" example:"WB7DRDWrJZRGbYrv2CKGkqbzvqdC"` + Contact string `json:"contact" example:"+18005550100"` + Content string `json:"content" example:"This is a sample text message"` + Attachments pq.StringArray `json:"attachments" gorm:"type:text[]" swaggertype:"array,string" example:"https://example.com/image.jpg,https://example.com/video.mp4"` + Encrypted bool `json:"encrypted" example:"false" gorm:"default:false"` + Type MessageType `json:"type" example:"mobile-terminated"` + Status MessageStatus `json:"status" example:"pending"` // SIM is the SIM card to use to send the message // * SMS1: use the SIM card in slot 1 // * SMS2: use the SIM card in slot 2 @@ -92,24 +101,24 @@ type Message struct { SIM SIM `json:"sim" example:"DEFAULT"` // SendDuration is the number of nanoseconds from when the request was received until when the mobile phone send the message - SendDuration *int64 `json:"send_time" example:"133414"` + SendDuration *int64 `json:"send_time" example:"133414" validate:"optional"` RequestReceivedAt time.Time `json:"request_received_at" example:"2022-06-05T14:26:01.520828+03:00"` CreatedAt time.Time `json:"created_at" example:"2022-06-05T14:26:02.302718+03:00"` UpdatedAt time.Time `json:"updated_at" example:"2022-06-05T14:26:10.303278+03:00"` OrderTimestamp time.Time `json:"order_timestamp" example:"2022-06-05T14:26:09.527976+03:00"` - LastAttemptedAt *time.Time `json:"last_attempted_at" example:"2022-06-05T14:26:09.527976+03:00"` - NotificationScheduledAt *time.Time `json:"scheduled_at" example:"2022-06-05T14:26:09.527976+03:00"` - SentAt *time.Time `json:"sent_at" example:"2022-06-05T14:26:09.527976+03:00"` - ScheduledSendTime *time.Time `json:"scheduled_send_time" example:"2022-06-05T14:26:09.527976+03:00"` - DeliveredAt *time.Time `json:"delivered_at" example:"2022-06-05T14:26:09.527976+03:00"` - ExpiredAt *time.Time `json:"expired_at" example:"2022-06-05T14:26:09.527976+03:00"` - FailedAt *time.Time `json:"failed_at" example:"2022-06-05T14:26:09.527976+03:00"` - CanBePolled bool `json:"can_be_polled" example:"false"` + LastAttemptedAt *time.Time `json:"last_attempted_at" example:"2022-06-05T14:26:09.527976+03:00" validate:"optional"` + NotificationScheduledAt *time.Time `json:"scheduled_at" example:"2022-06-05T14:26:09.527976+03:00" validate:"optional"` + SentAt *time.Time `json:"sent_at" example:"2022-06-05T14:26:09.527976+03:00" validate:"optional"` + ScheduledSendTime *time.Time `json:"scheduled_send_time" example:"2022-06-05T14:26:09.527976+03:00" validate:"optional"` + DeliveredAt *time.Time `json:"delivered_at" example:"2022-06-05T14:26:09.527976+03:00" validate:"optional"` + ExpiredAt *time.Time `json:"expired_at" example:"2022-06-05T14:26:09.527976+03:00" validate:"optional"` + FailedAt *time.Time `json:"failed_at" example:"2022-06-05T14:26:09.527976+03:00" validate:"optional"` + CanBePolled bool `json:"-" example:"false" swaggerignore:"true"` SendAttemptCount uint `json:"send_attempt_count" example:"0"` MaxSendAttempts uint `json:"max_send_attempts" example:"1"` - ReceivedAt *time.Time `json:"received_at" example:"2022-06-05T14:26:09.527976+03:00"` - FailureReason *string `json:"failure_reason" example:"UNKNOWN"` + ReceivedAt *time.Time `json:"received_at" example:"2022-06-05T14:26:09.527976+03:00" validate:"optional"` + FailureReason *string `json:"failure_reason" example:"UNKNOWN" validate:"optional"` } // IsSending determines if a message is being sent diff --git a/api/pkg/entities/message_thread.go b/api/pkg/entities/message_thread.go index 0766c5ed..2eb1e2d3 100644 --- a/api/pkg/entities/message_thread.go +++ b/api/pkg/entities/message_thread.go @@ -15,8 +15,8 @@ type MessageThread struct { UserID UserID `json:"user_id" example:"WB7DRDWrJZRGbYrv2CKGkqbzvqdC"` Color string `json:"color" example:"indigo"` Status MessageStatus `json:"status" example:"PENDING"` - LastMessageContent string `json:"last_message_content" example:"This is a sample message content"` - LastMessageID uuid.UUID `json:"last_message_id" example:"32343a19-da5e-4b1b-a767-3298a73703ca"` + LastMessageContent *string `json:"last_message_content" example:"This is a sample message content"` + LastMessageID *uuid.UUID `json:"last_message_id" example:"32343a19-da5e-4b1b-a767-3298a73703ca"` CreatedAt time.Time `json:"created_at" example:"2022-06-05T14:26:09.527976+03:00"` UpdatedAt time.Time `json:"updated_at" example:"2022-06-05T14:26:09.527976+03:00"` OrderTimestamp time.Time `json:"order_timestamp" example:"2022-06-05T14:26:09.527976+03:00"` @@ -25,9 +25,9 @@ type MessageThread struct { // Update a message thread after a message event func (thread *MessageThread) Update(timestamp time.Time, messageID uuid.UUID, content string, status MessageStatus) *MessageThread { thread.OrderTimestamp = timestamp - thread.LastMessageID = messageID + thread.LastMessageID = &messageID thread.Status = status - thread.LastMessageContent = content + thread.LastMessageContent = &content return thread } @@ -36,3 +36,11 @@ func (thread *MessageThread) UpdateArchive(isArchived bool) *MessageThread { thread.IsArchived = isArchived return thread } + +// HasLastMessage checks the last message in a thread by ID +func (thread *MessageThread) HasLastMessage(id uuid.UUID) bool { + if thread.LastMessageID == nil { + return false + } + return *thread.LastMessageID == id +} diff --git a/api/pkg/entities/phone.go b/api/pkg/entities/phone.go index b92ab1ab..83521759 100644 --- a/api/pkg/entities/phone.go +++ b/api/pkg/entities/phone.go @@ -10,7 +10,7 @@ import ( type Phone struct { ID uuid.UUID `json:"id" gorm:"primaryKey;type:uuid;" example:"32343a19-da5e-4b1b-a767-3298a73703cb"` UserID UserID `json:"user_id" example:"WB7DRDWrJZRGbYrv2CKGkqbzvqdC"` - FcmToken *string `json:"fcm_token" example:"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzd....."` + FcmToken *string `json:"fcm_token" example:"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzd....." validate:"optional"` PhoneNumber string `json:"phone_number" example:"+18005550199"` MessagesPerMinute uint `json:"messages_per_minute" example:"1"` SIM SIM `json:"sim" gorm:"default:SIM1"` @@ -20,6 +20,8 @@ type Phone struct { // MessageExpirationSeconds is the duration in seconds after sending a message when it is considered to be expired. MessageExpirationSeconds uint `json:"message_expiration_seconds"` + MissedCallAutoReply *string `json:"missed_call_auto_reply" example:"This phone cannot receive calls. Please send an SMS instead." validate:"optional"` + CreatedAt time.Time `json:"created_at" example:"2022-06-05T14:26:02.302718+03:00"` UpdatedAt time.Time `json:"updated_at" example:"2022-06-05T14:26:10.303278+03:00"` } diff --git a/api/pkg/entities/phone_api_key.go b/api/pkg/entities/phone_api_key.go new file mode 100644 index 00000000..5a32c234 --- /dev/null +++ b/api/pkg/entities/phone_api_key.go @@ -0,0 +1,26 @@ +package entities + +import ( + "time" + + "github.com/google/uuid" + "github.com/lib/pq" +) + +// PhoneAPIKey represents the API key for a phone +type PhoneAPIKey struct { + ID uuid.UUID `json:"id" gorm:"primaryKey;type:uuid;" example:"32343a19-da5e-4b1b-a767-3298a73703cb"` + Name string `json:"name" example:"Business Phone Key"` + UserID UserID `json:"user_id" example:"WB7DRDWrJZRGbYrv2CKGkqbzvqdC"` + UserEmail string `json:"user_email" example:"user@gmail.com"` + PhoneNumbers pq.StringArray `json:"phone_numbers" example:"+18005550199,+18005550100" gorm:"type:text[]" swaggertype:"array,string"` + PhoneIDs pq.StringArray `json:"phone_ids" example:"32343a19-da5e-4b1b-a767-3298a73703cb,32343a19-da5e-4b1b-a767-3298a73703cc" gorm:"type:text[]" swaggertype:"array,string"` + APIKey string `json:"api_key" gorm:"uniqueIndex:idx_phone_api_key__api_key;NOT NULL" example:"pk_DGW8NwQp7mxKaSZ72Xq9v6xxxxx"` + CreatedAt time.Time `json:"created_at" example:"2022-06-05T14:26:02.302718+03:00"` + UpdatedAt time.Time `json:"updated_at" example:"2022-06-05T14:26:02.302718+03:00"` +} + +// TableName overrides the table name used by PhoneAPIKey +func (PhoneAPIKey) TableName() string { + return "phone_api_keys" +} diff --git a/api/pkg/entities/user.go b/api/pkg/entities/user.go index 99225330..f26a8135 100644 --- a/api/pkg/entities/user.go +++ b/api/pkg/entities/user.go @@ -9,30 +9,32 @@ import ( // UserID is the ID of a user type UserID string +// String returns the string representation of a UserID +func (id UserID) String() string { + return string(id) +} + // SubscriptionName is the name of the subscription type SubscriptionName string // Limit returns the limit of a subscription func (subscription SubscriptionName) Limit() uint { - if subscription == SubscriptionNameFree { - return 200 - } - if subscription == SubscriptionNameProMonthly || subscription == SubscriptionNameProYearly || subscription == SubscriptionNameProLifetime { + switch subscription { + case SubscriptionNameProMonthly, SubscriptionNameProYearly, SubscriptionNameProLifetime: return 5000 - } - - if subscription == SubscriptionNameUltraMonthly || subscription == SubscriptionNameUltraYearly { + case SubscriptionNameUltraMonthly, SubscriptionNameUltraYearly: return 10_000 - } - - if subscription == SubscriptionName20KMonthly || subscription == SubscriptionName20KYearly { + case SubscriptionName20KMonthly, SubscriptionName20KYearly: return 20_000 - } - if subscription == SubscriptionName100KMonthly || subscription == SubscriptionName100KYearly { + case SubscriptionName50KMonthly: + return 50_000 + case SubscriptionName100KMonthly: return 100_000 + case SubscriptionName200KMonthly: + return 200_000 + default: + return 200 } - - return 200 } // SubscriptionNameFree represents a free subscription @@ -59,27 +61,31 @@ const SubscriptionName20KMonthly = SubscriptionName("20k-monthly") // SubscriptionName100KMonthly represents a monthly 100k subscription const SubscriptionName100KMonthly = SubscriptionName("100k-monthly") +// SubscriptionName50KMonthly represents a monthly 50k subscription +const SubscriptionName50KMonthly = SubscriptionName("50k-monthly") + +// SubscriptionName200KMonthly represents a monthly 200k subscription +const SubscriptionName200KMonthly = SubscriptionName("200k-monthly") + // SubscriptionName20KYearly represents a yearly 20k subscription const SubscriptionName20KYearly = SubscriptionName("20k-yearly") -// SubscriptionName100KYearly represents a yearly 100k subscription -const SubscriptionName100KYearly = SubscriptionName("100k-yearly") - // User stores information about a user type User struct { ID UserID `json:"id" gorm:"primaryKey;type:string;" example:"WB7DRDWrJZRGbYrv2CKGkqbzvqdC"` Email string `json:"email" example:"name@email.com"` - APIKey string `json:"api_key" example:"xyz"` + APIKey string `json:"api_key" gorm:"uniqueIndex:idx_users_api_key;NOT NULL" example:"x-api-key"` Timezone string `json:"timezone" example:"Europe/Helsinki" gorm:"default:Africa/Accra"` - ActivePhoneID *uuid.UUID `json:"active_phone_id" gorm:"type:uuid;" example:"32343a19-da5e-4b1b-a767-3298a73703cb"` + ActivePhoneID *uuid.UUID `json:"active_phone_id" gorm:"type:uuid;" example:"32343a19-da5e-4b1b-a767-3298a73703cb" validate:"optional"` SubscriptionName SubscriptionName `json:"subscription_name" example:"free"` SubscriptionID *string `json:"subscription_id" example:"8f9c71b8-b84e-4417-8408-a62274f65a08"` - SubscriptionStatus *string `json:"subscription_status" example:"on_trial"` - SubscriptionRenewsAt *time.Time `json:"subscription_renews_at" example:"2022-06-05T14:26:02.302718+03:00"` - SubscriptionEndsAt *time.Time `json:"subscription_ends_at" example:"2022-06-05T14:26:02.302718+03:00"` + SubscriptionStatus *string `json:"subscription_status" example:"on_trial" validate:"optional"` + SubscriptionRenewsAt *time.Time `json:"subscription_renews_at" example:"2022-06-05T14:26:02.302718+03:00" validate:"optional"` + SubscriptionEndsAt *time.Time `json:"subscription_ends_at" example:"2022-06-05T14:26:02.302718+03:00" validate:"optional"` NotificationMessageStatusEnabled bool `json:"notification_message_status_enabled" gorm:"default:true" example:"true"` NotificationWebhookEnabled bool `json:"notification_webhook_enabled" gorm:"default:true" example:"true"` NotificationHeartbeatEnabled bool `json:"notification_heartbeat_enabled" gorm:"default:true" example:"true"` + NotificationNewsletterEnabled bool `json:"notification_newsletter_enabled" gorm:"default:true" example:"true"` CreatedAt time.Time `json:"created_at" example:"2022-06-05T14:26:02.302718+03:00"` UpdatedAt time.Time `json:"updated_at" example:"2022-06-05T14:26:10.303278+03:00"` } @@ -89,11 +95,21 @@ func (user User) IsOnProPlan() bool { return user.SubscriptionName == SubscriptionNameProLifetime || user.SubscriptionName == SubscriptionNameProMonthly || user.SubscriptionName == SubscriptionNameProYearly } +// IsOnFreePlan checks if a user is on the free plan +func (user User) IsOnFreePlan() bool { + return user.SubscriptionName == SubscriptionNameFree || user.SubscriptionName == "" +} + // IsOnUltraPlan checks if a user is on the ultra plan func (user User) IsOnUltraPlan() bool { return user.SubscriptionName == SubscriptionNameUltraMonthly || user.SubscriptionName == SubscriptionNameUltraYearly } +// IsOn20kPlan checks if a user is on the 20k plan +func (user User) IsOn20kPlan() bool { + return user.SubscriptionName == SubscriptionName20KMonthly || user.SubscriptionName == SubscriptionName20KYearly +} + // UserTimeString converts the time to the user's timezone func (user User) UserTimeString(timestamp time.Time) string { location, err := time.LoadLocation(user.Timezone) diff --git a/api/pkg/entities/webhook.go b/api/pkg/entities/webhook.go index a1595849..7b98f676 100644 --- a/api/pkg/entities/webhook.go +++ b/api/pkg/entities/webhook.go @@ -13,8 +13,8 @@ type Webhook struct { UserID UserID `json:"user_id" example:"WB7DRDWrJZRGbYrv2CKGkqbzvqdC"` URL string `json:"url" example:"https://example.com"` SigningKey string `json:"signing_key" example:"DGW8NwQp7mxKaSZ72Xq9v67SLqSbWQvckzzmK8D6rvd7NywSEkdMJtuxKyEkYnCY"` - PhoneNumbers pq.StringArray `json:"phone_numbers" example:"[+18005550199,+18005550100]" gorm:"type:text[]" swaggertype:"array,string"` - Events pq.StringArray `json:"events" example:"[message.phone.received]" gorm:"type:text[]" swaggertype:"array,string"` + PhoneNumbers pq.StringArray `json:"phone_numbers" example:"+18005550199,+18005550100" gorm:"type:text[]" swaggertype:"array,string"` + Events pq.StringArray `json:"events" example:"message.phone.received" gorm:"type:text[]" swaggertype:"array,string"` CreatedAt time.Time `json:"created_at" example:"2022-06-05T14:26:02.302718+03:00"` UpdatedAt time.Time `json:"updated_at" example:"2022-06-05T14:26:10.303278+03:00"` } diff --git a/api/pkg/events/message_api_deleted_event.go b/api/pkg/events/message_api_deleted_event.go new file mode 100644 index 00000000..e0de354f --- /dev/null +++ b/api/pkg/events/message_api_deleted_event.go @@ -0,0 +1,28 @@ +package events + +import ( + "time" + + "github.com/NdoleStudio/httpsms/pkg/entities" + + "github.com/google/uuid" +) + +// MessageAPIDeleted is emitted when a new message is deleted +const MessageAPIDeleted = "message.api.deleted" + +// MessageAPIDeletedPayload is the payload of the MessageAPIDeleted event +type MessageAPIDeletedPayload struct { + MessageID uuid.UUID `json:"message_id"` + UserID entities.UserID `json:"user_id"` + Owner string `json:"owner"` + RequestID *string `json:"request_id"` + Contact string `json:"contact"` + Timestamp time.Time `json:"timestamp"` + Content string `json:"content"` + Encrypted bool `json:"encrypted"` + PreviousMessageID *uuid.UUID `json:"previous_message_id"` + PreviousMessageStatus *entities.MessageStatus `json:"previous_message_status"` + PreviousMessageContent *string `json:"previous_message_content"` + SIM entities.SIM `json:"sim"` +} diff --git a/api/pkg/events/message_api_sent_event.go b/api/pkg/events/message_api_sent_event.go index 27f8a540..60a418b2 100644 --- a/api/pkg/events/message_api_sent_event.go +++ b/api/pkg/events/message_api_sent_event.go @@ -22,5 +22,7 @@ type MessageAPISentPayload struct { ScheduledSendTime *time.Time `json:"scheduled_send_time"` RequestReceivedAt time.Time `json:"request_received_at"` Content string `json:"content"` + Attachments []string `json:"attachments"` + Encrypted bool `json:"encrypted"` SIM entities.SIM `json:"sim"` } diff --git a/api/pkg/events/message_call_missed_event.go b/api/pkg/events/message_call_missed_event.go new file mode 100644 index 00000000..840b9a1d --- /dev/null +++ b/api/pkg/events/message_call_missed_event.go @@ -0,0 +1,22 @@ +package events + +import ( + "time" + + "github.com/NdoleStudio/httpsms/pkg/entities" + + "github.com/google/uuid" +) + +// MessageCallMissed is emitted when a new message is sent +const MessageCallMissed = "message.call.missed" + +// MessageCallMissedPayload is the payload of the MessageCallMissed event +type MessageCallMissedPayload struct { + MessageID uuid.UUID `json:"message_id"` + UserID entities.UserID `json:"user_id"` + Owner string `json:"owner"` + Contact string `json:"contact"` + Timestamp time.Time `json:"timestamp"` + SIM entities.SIM `json:"sim"` +} diff --git a/api/pkg/events/message_notification_scheduled_event.go b/api/pkg/events/message_notification_scheduled_event.go index e8bcc598..50171f3f 100644 --- a/api/pkg/events/message_notification_scheduled_event.go +++ b/api/pkg/events/message_notification_scheduled_event.go @@ -17,6 +17,7 @@ type MessageNotificationScheduledPayload struct { Owner string `json:"owner"` Contact string `json:"contact"` Content string `json:"content"` + Encrypted bool `json:"encrypted"` SIM entities.SIM `json:"sim"` UserID entities.UserID `json:"user_id"` PhoneID uuid.UUID `json:"phone_id"` diff --git a/api/pkg/events/message_phone_delivered_event.go b/api/pkg/events/message_phone_delivered_event.go index 0c68df81..a8512653 100644 --- a/api/pkg/events/message_phone_delivered_event.go +++ b/api/pkg/events/message_phone_delivered_event.go @@ -18,6 +18,7 @@ type MessagePhoneDeliveredPayload struct { Contact string `json:"contact"` RequestID *string `json:"request_id"` UserID entities.UserID `json:"user_id"` + Encrypted bool `json:"encrypted"` Timestamp time.Time `json:"timestamp"` Content string `json:"content"` SIM entities.SIM `json:"sim"` diff --git a/api/pkg/events/message_phone_received_event.go b/api/pkg/events/message_phone_received_event.go index 9159e4fc..04dd6c2e 100644 --- a/api/pkg/events/message_phone_received_event.go +++ b/api/pkg/events/message_phone_received_event.go @@ -13,11 +13,13 @@ const EventTypeMessagePhoneReceived = "message.phone.received" // MessagePhoneReceivedPayload is the payload of the EventTypeMessagePhoneReceived event type MessagePhoneReceivedPayload struct { - MessageID uuid.UUID `json:"message_id"` - UserID entities.UserID `json:"user_id"` - Owner string `json:"owner"` - Contact string `json:"contact"` - Timestamp time.Time `json:"timestamp"` - Content string `json:"content"` - SIM entities.SIM `json:"sim"` + MessageID uuid.UUID `json:"message_id"` + UserID entities.UserID `json:"user_id"` + Owner string `json:"owner"` + Encrypted bool `json:"encrypted"` + Contact string `json:"contact"` + Timestamp time.Time `json:"timestamp"` + Content string `json:"content"` + SIM entities.SIM `json:"sim"` + Attachments []string `json:"attachments"` } diff --git a/api/pkg/events/message_phone_sending_event.go b/api/pkg/events/message_phone_sending_event.go index 31915634..c6615b93 100644 --- a/api/pkg/events/message_phone_sending_event.go +++ b/api/pkg/events/message_phone_sending_event.go @@ -17,6 +17,7 @@ type MessagePhoneSendingPayload struct { RequestID *string `json:"request_id"` Timestamp time.Time `json:"timestamp"` Owner string `json:"owner"` + Encrypted bool `json:"encrypted"` Contact string `json:"contact"` Content string `json:"content"` SIM entities.SIM `json:"sim"` diff --git a/api/pkg/events/message_phone_sent_event.go b/api/pkg/events/message_phone_sent_event.go index fcbd95ee..f9f3fa4b 100644 --- a/api/pkg/events/message_phone_sent_event.go +++ b/api/pkg/events/message_phone_sent_event.go @@ -18,6 +18,7 @@ type MessagePhoneSentPayload struct { RequestID *string `json:"request_id"` Owner string `json:"owner"` Contact string `json:"contact"` + Encrypted bool `json:"encrypted"` Timestamp time.Time `json:"timestamp"` Content string `json:"content"` SIM entities.SIM `json:"sim"` diff --git a/api/pkg/events/message_send_expired_event.go b/api/pkg/events/message_send_expired_event.go index 697deadd..ec51fcfa 100644 --- a/api/pkg/events/message_send_expired_event.go +++ b/api/pkg/events/message_send_expired_event.go @@ -19,6 +19,7 @@ type MessageSendExpiredPayload struct { IsFinal bool `json:"is_final"` RequestID *string `json:"request_id"` Contact string `json:"contact"` + Encrypted bool `json:"encrypted"` UserID entities.UserID `json:"user_id"` Timestamp time.Time `json:"timestamp"` Content string `json:"content"` diff --git a/api/pkg/events/message_send_failed_event.go b/api/pkg/events/message_send_failed_event.go index 00a12552..fbc6836f 100644 --- a/api/pkg/events/message_send_failed_event.go +++ b/api/pkg/events/message_send_failed_event.go @@ -19,6 +19,7 @@ type MessageSendFailedPayload struct { RequestID *string `json:"request_id"` Contact string `json:"contact"` Timestamp time.Time `json:"timestamp"` + Encrypted bool `json:"encrypted"` Content string `json:"content"` SIM entities.SIM `json:"sim"` } diff --git a/api/pkg/events/message_send_retry_event.go b/api/pkg/events/message_send_retry_event.go index 05549bea..e60c5c7b 100644 --- a/api/pkg/events/message_send_retry_event.go +++ b/api/pkg/events/message_send_retry_event.go @@ -16,6 +16,7 @@ type MessageSendRetryPayload struct { MessageID uuid.UUID `json:"message_id"` Owner string `json:"owner"` Contact string `json:"contact"` + Encrypted bool `json:"encrypted"` UserID entities.UserID `json:"user_id"` Timestamp time.Time `json:"timestamp"` Content string `json:"content"` diff --git a/api/pkg/events/message_thead_api_deleted_event.go b/api/pkg/events/message_thead_api_deleted_event.go new file mode 100644 index 00000000..aeb79273 --- /dev/null +++ b/api/pkg/events/message_thead_api_deleted_event.go @@ -0,0 +1,24 @@ +package events + +import ( + "time" + + "github.com/NdoleStudio/httpsms/pkg/entities" + + "github.com/google/uuid" +) + +// MessageThreadAPIDeleted is emitted when a new message is deleted +const MessageThreadAPIDeleted = "message-thread.api.deleted" + +// MessageThreadAPIDeletedPayload is the payload of the MessageThreadAPIDeleted event +type MessageThreadAPIDeletedPayload struct { + MessageThreadID uuid.UUID `json:"message_thread_id"` + UserID entities.UserID `json:"user_id"` + Owner string `json:"owner"` + Contact string `json:"contact"` + IsArchived bool `json:"is_archived"` + Color string `json:"color"` + Status entities.MessageStatus `json:"status"` + Timestamp time.Time `json:"timestamp"` +} diff --git a/api/pkg/events/phone_heartbeat_offline_event.go b/api/pkg/events/phone_heartbeat_offline_event.go new file mode 100644 index 00000000..d5eed76e --- /dev/null +++ b/api/pkg/events/phone_heartbeat_offline_event.go @@ -0,0 +1,21 @@ +package events + +import ( + "time" + + "github.com/NdoleStudio/httpsms/pkg/entities" + "github.com/google/uuid" +) + +// EventTypePhoneHeartbeatOffline is emitted when the phone is missing a heartbeat +const EventTypePhoneHeartbeatOffline = "phone.heartbeat.offline" + +// PhoneHeartbeatOfflinePayload is the payload of the EventTypePhoneHeartbeatOffline event +type PhoneHeartbeatOfflinePayload struct { + PhoneID uuid.UUID `json:"phone_id"` + UserID entities.UserID `json:"user_id"` + LastHeartbeatTimestamp time.Time `json:"last_heartbeat_timestamp"` + Timestamp time.Time `json:"timestamp"` + MonitorID uuid.UUID `json:"monitor_id"` + Owner string `json:"owner"` +} diff --git a/api/pkg/events/phone_heartbeat_dead_event.go b/api/pkg/events/phone_heartbeat_online_event.go similarity index 63% rename from api/pkg/events/phone_heartbeat_dead_event.go rename to api/pkg/events/phone_heartbeat_online_event.go index bd6989aa..13fab5eb 100644 --- a/api/pkg/events/phone_heartbeat_dead_event.go +++ b/api/pkg/events/phone_heartbeat_online_event.go @@ -7,11 +7,11 @@ import ( "github.com/google/uuid" ) -// EventTypePhoneHeartbeatDead is emitted when the phone is missing a heartbeat -const EventTypePhoneHeartbeatDead = "phone.heartbeat.dead" +// EventTypePhoneHeartbeatOnline is emitted when the phone is missing a heartbeat +const EventTypePhoneHeartbeatOnline = "phone.heartbeat.online" -// PhoneHeartbeatDeadPayload is the payload of the EventTypePhoneHeartbeatDead event -type PhoneHeartbeatDeadPayload struct { +// PhoneHeartbeatOnlinePayload is the payload of the EventTypePhoneHeartbeatOnline event +type PhoneHeartbeatOnlinePayload struct { PhoneID uuid.UUID `json:"phone_id"` UserID entities.UserID `json:"user_id"` LastHeartbeatTimestamp time.Time `json:"last_heartbeat_timestamp"` diff --git a/api/pkg/events/phone_updated_event.go b/api/pkg/events/phone_updated_event.go index 88fc6c9d..082aaa3a 100644 --- a/api/pkg/events/phone_updated_event.go +++ b/api/pkg/events/phone_updated_event.go @@ -12,9 +12,10 @@ const EventTypePhoneUpdated = "phone.updated" // PhoneUpdatedPayload is the payload of the EventTypePhoneUpdated event type PhoneUpdatedPayload struct { - PhoneID uuid.UUID `json:"phone_id"` - UserID entities.UserID `json:"user_id"` - Timestamp time.Time `json:"timestamp"` - Owner string `json:"owner"` - SIM entities.SIM `json:"sim"` + PhoneID uuid.UUID `json:"phone_id"` + UserID entities.UserID `json:"user_id"` + PhoneAPIKeyID *uuid.UUID `json:"phone_api_key_id"` + Timestamp time.Time `json:"timestamp"` + Owner string `json:"owner"` + SIM entities.SIM `json:"sim"` } diff --git a/api/pkg/events/user_account_created_event.go b/api/pkg/events/user_account_created_event.go new file mode 100644 index 00000000..2b37ef8a --- /dev/null +++ b/api/pkg/events/user_account_created_event.go @@ -0,0 +1,16 @@ +package events + +import ( + "time" + + "github.com/NdoleStudio/httpsms/pkg/entities" +) + +// UserAccountCreated is raised when a user's account is created. +const UserAccountCreated = "user.account.created" + +// UserAccountCreatedPayload stores the data for the UserAccountCreated event +type UserAccountCreatedPayload struct { + UserID entities.UserID `json:"user_id"` + Timestamp time.Time `json:"timestamp"` +} diff --git a/api/pkg/events/user_account_deleted_event.go b/api/pkg/events/user_account_deleted_event.go new file mode 100644 index 00000000..bf8f68db --- /dev/null +++ b/api/pkg/events/user_account_deleted_event.go @@ -0,0 +1,17 @@ +package events + +import ( + "time" + + "github.com/NdoleStudio/httpsms/pkg/entities" +) + +// UserAccountDeleted is raised when a user's account is deleted. +const UserAccountDeleted = "user.account.deleted" + +// UserAccountDeletedPayload stores the data for the UserAccountDeleted event +type UserAccountDeletedPayload struct { + UserID entities.UserID `json:"user_id"` + UserEmail string `json:"user_email"` + Timestamp time.Time `json:"timestamp"` +} diff --git a/api/pkg/events/user_api_key_rotated_event.go b/api/pkg/events/user_api_key_rotated_event.go new file mode 100644 index 00000000..a5df1e0f --- /dev/null +++ b/api/pkg/events/user_api_key_rotated_event.go @@ -0,0 +1,18 @@ +package events + +import ( + "time" + + "github.com/NdoleStudio/httpsms/pkg/entities" +) + +// UserAPIKeyRotated is raised when a user's API key is rotated +const UserAPIKeyRotated = "user.api-key.rotated" + +// UserAPIKeyRotatedPayload stores the data for the UserAPIKeyRotated event +type UserAPIKeyRotatedPayload struct { + UserID entities.UserID `json:"user_id"` + Email string `json:"email"` + Timestamp time.Time `json:"timestamp"` + Timezone string `json:"timezone"` +} diff --git a/api/pkg/events/user_subscription_updated_event.go b/api/pkg/events/user_subscription_updated_event.go new file mode 100644 index 00000000..bc50a010 --- /dev/null +++ b/api/pkg/events/user_subscription_updated_event.go @@ -0,0 +1,21 @@ +package events + +import ( + "time" + + "github.com/NdoleStudio/httpsms/pkg/entities" +) + +// UserSubscriptionUpdated is raised when a user subscription is updated +const UserSubscriptionUpdated = "user.subscription.updated" + +// UserSubscriptionUpdatedPayload stores the data for the UserSubscriptionUpdated event +type UserSubscriptionUpdatedPayload struct { + UserID entities.UserID `json:"user_id"` + SubscriptionUpdatedAt time.Time `json:"subscription_updated_at"` + SubscriptionEndsAt *time.Time `json:"subscription_ends_at"` + SubscriptionRenewsAt time.Time `json:"subscription_renews_at"` + SubscriptionID string `json:"subscription_id"` + SubscriptionName entities.SubscriptionName `json:"subscription_name"` + SubscriptionStatus string `json:"subscription_status"` +} diff --git a/api/pkg/events/webhook_event_failed_event.go b/api/pkg/events/webhook_event_failed_event.go index 706b58aa..8473190f 100644 --- a/api/pkg/events/webhook_event_failed_event.go +++ b/api/pkg/events/webhook_event_failed_event.go @@ -16,6 +16,7 @@ type WebhookSendFailedPayload struct { UserID entities.UserID `json:"user_id"` EventID string `json:"event_id"` EventType string `json:"event_type"` + EventPayload string `json:"event_payload"` HTTPResponseStatusCode *int `json:"http_response_status_code"` ErrorMessage string `json:"error_message"` } diff --git a/api/pkg/handlers/attachment_handler.go b/api/pkg/handlers/attachment_handler.go new file mode 100644 index 00000000..46a4397b --- /dev/null +++ b/api/pkg/handlers/attachment_handler.go @@ -0,0 +1,85 @@ +package handlers + +import ( + "fmt" + "path/filepath" + + "github.com/NdoleStudio/httpsms/pkg/repositories" + "github.com/NdoleStudio/httpsms/pkg/telemetry" + "github.com/gofiber/fiber/v2" + "github.com/palantir/stacktrace" +) + +// AttachmentHandler handles attachment download requests +type AttachmentHandler struct { + handler + logger telemetry.Logger + tracer telemetry.Tracer + storage repositories.AttachmentRepository +} + +// NewAttachmentHandler creates a new AttachmentHandler +func NewAttachmentHandler( + logger telemetry.Logger, + tracer telemetry.Tracer, + storage repositories.AttachmentRepository, +) (h *AttachmentHandler) { + return &AttachmentHandler{ + logger: logger.WithService(fmt.Sprintf("%T", h)), + tracer: tracer, + storage: storage, + } +} + +// RegisterRoutes registers the routes for the AttachmentHandler (no auth middleware — public endpoint) +func (h *AttachmentHandler) RegisterRoutes(router fiber.Router) { + router.Get("/v1/attachments/:userID/:messageID/:attachmentIndex/:filename", h.GetAttachment) +} + +// GetAttachment Downloads an attachment +// @Summary Download a message attachment +// @Description Download an MMS attachment by its path components +// @Tags Attachments +// @Produce application/octet-stream +// @Param userID path string true "User ID" +// @Param messageID path string true "Message ID" +// @Param attachmentIndex path string true "Attachment index" +// @Param filename path string true "Filename with extension" +// @Success 200 {file} binary +// @Failure 404 {object} responses.NotFound +// @Failure 500 {object} responses.InternalServerError +// @Router /v1/attachments/{userID}/{messageID}/{attachmentIndex}/{filename} [get] +func (h *AttachmentHandler) GetAttachment(c *fiber.Ctx) error { + ctx, span := h.tracer.StartFromFiberCtx(c) + defer span.End() + + ctxLogger := h.tracer.CtxLogger(h.logger, span) + + userID := c.Params("userID") + messageID := c.Params("messageID") + attachmentIndex := c.Params("attachmentIndex") + filename := c.Params("filename") + + path := fmt.Sprintf("attachments/%s/%s/%s/%s", userID, messageID, attachmentIndex, filename) + + ctxLogger.Info(fmt.Sprintf("downloading attachment from path [%s]", path)) + + data, err := h.storage.Download(ctx, path) + if err != nil { + msg := fmt.Sprintf("cannot download attachment from path [%s]", path) + ctxLogger.Warn(stacktrace.Propagate(err, msg)) + if stacktrace.GetCode(err) == repositories.ErrCodeNotFound { + return h.responseNotFound(c, "attachment not found") + } + return h.responseInternalServerError(c) + } + + ext := filepath.Ext(filename) + contentType := repositories.ContentTypeFromExtension(ext) + + c.Set("Content-Type", contentType) + c.Set("Content-Disposition", "attachment") + c.Set("X-Content-Type-Options", "nosniff") + + return c.Send(data) +} diff --git a/api/pkg/handlers/billing_handler.go b/api/pkg/handlers/billing_handler.go index cd6d5713..3d65ee9a 100644 --- a/api/pkg/handlers/billing_handler.go +++ b/api/pkg/handlers/billing_handler.go @@ -37,9 +37,9 @@ func NewBillingHandler( } // RegisterRoutes registers the routes for the MessageHandler -func (h *BillingHandler) RegisterRoutes(router fiber.Router) { - router.Get("/billing/usage-history", h.UsageHistory) - router.Get("/billing/usage", h.Usage) +func (h *BillingHandler) RegisterRoutes(router fiber.Router, middlewares ...fiber.Handler) { + router.Get("/v1/billing/usage-history", h.computeRoute(middlewares, h.UsageHistory)...) + router.Get("/v1/billing/usage", h.computeRoute(middlewares, h.Usage)...) } // UsageHistory returns the usage history of a user @@ -65,7 +65,7 @@ func (h *BillingHandler) UsageHistory(c *fiber.Ctx) error { var request requests.BillingUsageHistory if err := c.QueryParser(&request); err != nil { - msg := fmt.Sprintf("cannot marshall params [%s] into %T", c.OriginalURL(), request) + msg := fmt.Sprintf("cannot marshall params [%s] into %T", c.Body(), request) ctxLogger.Warn(stacktrace.Propagate(err, msg)) return h.responseBadRequest(c, err) } diff --git a/api/pkg/handlers/bulk_message_handler.go b/api/pkg/handlers/bulk_message_handler.go index fa3cce82..c660eeaa 100644 --- a/api/pkg/handlers/bulk_message_handler.go +++ b/api/pkg/handlers/bulk_message_handler.go @@ -3,6 +3,7 @@ package handlers import ( "fmt" "sync" + "sync/atomic" "github.com/NdoleStudio/httpsms/pkg/requests" "github.com/google/uuid" @@ -43,17 +44,18 @@ func NewBulkMessageHandler( } // RegisterRoutes registers the routes for the MessageHandler -func (h *BulkMessageHandler) RegisterRoutes(router fiber.Router) { - router.Post("/bulk-messages", h.Store) +func (h *BulkMessageHandler) RegisterRoutes(router fiber.Router, middlewares ...fiber.Handler) { + router.Post("/v1/bulk-messages", h.computeRoute(middlewares, h.Store)...) } -// Store sends bulk SMS messages from a CSV file. +// Store sends bulk SMS messages from a CSV or Excel file. // @Summary Store bulk SMS file -// @Description Sends bulk SMS messages to multiple users from a CSV file. +// @Description Sends bulk SMS messages to multiple users based on our [CSV template](https://httpsms.com/templates/httpsms-bulk.csv) or our [Excel template](https://httpsms.com/templates/httpsms-bulk.xlsx). // @Security ApiKeyAuth // @Tags BulkSMS -// @Accept json +// @Accept multipart/form-data // @Produce json +// @Param document formData file true "The Excel or CSV file containing the messages to be sent." // @Success 202 {object} responses.NoContent // @Failure 400 {object} responses.BadRequest // @Failure 401 {object} responses.Unauthorized @@ -85,22 +87,25 @@ func (h *BulkMessageHandler) Store(c *fiber.Ctx) error { requestID := uuid.New() wg := sync.WaitGroup{} - for _, message := range messages { + count := atomic.Int64{} + + for index, message := range messages { wg.Add(1) - go func(message *requests.BulkMessage) { + go func(message *requests.BulkMessage, index int) { + count.Add(1) _, err = h.messageService.SendMessage( ctx, message.ToMessageSendParams(h.userIDFomContext(c), requestID, c.OriginalURL()), ) - if err != nil { - msg := fmt.Sprintf("cannot send message with paylod [%s]", c.Body()) + count.Add(-1) + msg := fmt.Sprintf("cannot send message with paylod [%s] at index [%d]", spew.Sdump(message), index) ctxLogger.Error(stacktrace.Propagate(err, msg)) } wg.Done() - }(message) + }(message, index) } wg.Wait() - return h.responseAccepted(c, fmt.Sprintf("Added %d messages to the queue", len(messages))) + return h.responseAccepted(c, fmt.Sprintf("Added %d out of %d messages to the queue", count.Load(), len(messages))) } diff --git a/api/pkg/handlers/discord_handler.go b/api/pkg/handlers/discord_handler.go index 28df3ef1..95591c2b 100644 --- a/api/pkg/handlers/discord_handler.go +++ b/api/pkg/handlers/discord_handler.go @@ -128,7 +128,7 @@ func (h *DiscordHandler) Delete(c *fiber.Ctx) error { defer span.End() discordID := c.Params("discordID") - if errors := h.validator.ValidateUUID(ctx, discordID, "discordID"); len(errors) != 0 { + if errors := h.validator.ValidateUUID(discordID, "discordID"); len(errors) != 0 { msg := fmt.Sprintf("validation errors [%s], while deleting discord integration with ID [%s]", spew.Sdump(errors), discordID) ctxLogger.Warn(stacktrace.NewError(msg)) return h.responseUnprocessableEntity(c, errors, "validation errors while deleting discord integration") diff --git a/api/pkg/handlers/events_handler.go b/api/pkg/handlers/events_handler.go index 204fcc35..16d0325d 100644 --- a/api/pkg/handlers/events_handler.go +++ b/api/pkg/handlers/events_handler.go @@ -37,8 +37,8 @@ func NewEventsHandler( } // RegisterRoutes registers the routes for the MessageHandler -func (h *EventsHandler) RegisterRoutes(router fiber.Router) { - router.Post("/events", h.Dispatch) +func (h *EventsHandler) RegisterRoutes(router fiber.Router, middlewares ...fiber.Handler) { + router.Post("/v1/events", h.computeRoute(middlewares, h.Dispatch)...) } // Dispatch a cloud event diff --git a/api/pkg/handlers/handler.go b/api/pkg/handlers/handler.go index c5e15388..e6d4cc09 100644 --- a/api/pkg/handlers/handler.go +++ b/api/pkg/handlers/handler.go @@ -1,7 +1,10 @@ package handlers import ( + "fmt" "net/url" + "slices" + "strings" "github.com/NdoleStudio/httpsms/pkg/entities" "github.com/NdoleStudio/httpsms/pkg/middlewares" @@ -35,6 +38,14 @@ func (h *handler) responseUnauthorized(c *fiber.Ctx) error { }) } +func (h *handler) responsePhoneAPIKeyUnauthorized(c *fiber.Ctx, owner string, authCtx entities.AuthContext) error { + return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ + "status": "error", + "message": "You are not authorized to carry out the request for this phone number", + "data": fmt.Sprintf("The phone API key is does not have permission to carry out actions on the phone number [%s]. The API key is only configured for these phone numbers [%s]", owner, strings.Join(authCtx.PhoneNumbers, ",")), + }) +} + func (h *handler) responseForbidden(c *fiber.Ctx) error { return c.Status(fiber.StatusForbidden).JSON(fiber.Map{ "status": "error", @@ -101,8 +112,8 @@ func (h *handler) pluralize(value string, count int) string { return value + "s" } -func (h *handler) userFromContext(c *fiber.Ctx) entities.AuthUser { - if tokenUser, ok := c.Locals(middlewares.ContextKeyAuthUserID).(entities.AuthUser); ok && !tokenUser.IsNoop() { +func (h *handler) userFromContext(c *fiber.Ctx) entities.AuthContext { + if tokenUser, ok := c.Locals(middlewares.ContextKeyAuthUserID).(entities.AuthContext); ok && !tokenUser.IsNoop() { return tokenUser } panic("user does not exist in context.") @@ -115,3 +126,23 @@ func (h *handler) userIDFomContext(c *fiber.Ctx) entities.UserID { func (h *handler) computeRoute(middlewares []fiber.Handler, route fiber.Handler) []fiber.Handler { return append(append([]fiber.Handler{}, middlewares...), route) } + +func (h *handler) mergeErrors(errors ...url.Values) url.Values { + result := url.Values{} + for _, item := range errors { + for key, values := range item { + for _, value := range values { + result.Add(key, value) + } + } + } + return result +} + +func (h *handler) authorizePhoneAPIKey(c *fiber.Ctx, phoneNumber string) bool { + user := h.userFromContext(c) + if user.PhoneAPIKeyID == nil { + return true + } + return slices.Contains(user.PhoneNumbers, phoneNumber) +} diff --git a/api/pkg/handlers/handler_test.go b/api/pkg/handlers/handler_test.go new file mode 100644 index 00000000..a8aab4c6 --- /dev/null +++ b/api/pkg/handlers/handler_test.go @@ -0,0 +1,13 @@ +package handlers + +import ( + "os" + + "github.com/carlmjohnson/requests" + _ "github.com/joho/godotenv/autoload" // import USER_API_KEY from .env file +) + +func testClient() *requests.Builder { + return requests.URL("http://localhost:8000"). + Header("x-api-key", os.Getenv("USER_API_KEY")) +} diff --git a/api/pkg/handlers/heartbeat_handler.go b/api/pkg/handlers/heartbeat_handler.go index 27bd4b2c..f84cc0f9 100644 --- a/api/pkg/handlers/heartbeat_handler.go +++ b/api/pkg/handlers/heartbeat_handler.go @@ -2,6 +2,9 @@ package handlers import ( "fmt" + "sync" + + "github.com/NdoleStudio/httpsms/pkg/entities" "github.com/NdoleStudio/httpsms/pkg/requests" "github.com/NdoleStudio/httpsms/pkg/services" @@ -36,10 +39,14 @@ func NewHeartbeatHandler( } } -// RegisterRoutes registers the routes for the MessageHandler -func (h *HeartbeatHandler) RegisterRoutes(router fiber.Router) { - router.Get("/heartbeats", h.Index) - router.Post("/heartbeats", h.Store) +// RegisterRoutes registers the routes for the HeartbeatHandler +func (h *HeartbeatHandler) RegisterRoutes(router fiber.Router, middlewares ...fiber.Handler) { + router.Get("/v1/heartbeats", h.computeRoute(middlewares, h.Index)...) +} + +// RegisterPhoneAPIKeyRoutes registers the routes for the HeartbeatHandler +func (h *HeartbeatHandler) RegisterPhoneAPIKeyRoutes(router fiber.Router, middlewares ...fiber.Handler) { + router.Post("/v1/heartbeats", h.computeRoute(middlewares, h.Store)...) } // Index returns the heartbeats of a phone number @@ -121,12 +128,30 @@ func (h *HeartbeatHandler) Store(c *fiber.Ctx) error { return h.responseUnprocessableEntity(c, errors, "validation errors while storing heartbeat") } - heartbeat, err := h.service.Store(ctx, request.ToStoreParams(h.userFromContext(c), c.Get("X-Client-Version"))) - if err != nil { - msg := fmt.Sprintf("cannot store heartbeat with params [%+#v]", request) - ctxLogger.Error(stacktrace.Propagate(err, msg)) - return h.responseInternalServerError(c) + for _, phoneNumber := range request.PhoneNumbers { + if !h.authorizePhoneAPIKey(c, phoneNumber) { + ctxLogger.Warn(stacktrace.NewError(fmt.Sprintf("phone API Key ID [%s] is not authorized to store heartbeat for phone number [%s]", h.userFromContext(c).PhoneAPIKeyID, phoneNumber))) + return h.responsePhoneAPIKeyUnauthorized(c, phoneNumber, h.userFromContext(c)) + } + } + + params := request.ToStoreParams(h.userFromContext(c), c.OriginalURL(), c.Get("X-Client-Version")) + + wg := sync.WaitGroup{} + responses := make([]*entities.Heartbeat, len(params)) + for index, value := range params { + wg.Add(1) + go func(input services.HeartbeatStoreParams, index int) { + response, err := h.service.Store(ctx, input) + if err != nil { + msg := fmt.Sprintf("cannot store heartbeat with params [%+#v]", request) + ctxLogger.Error(stacktrace.Propagate(err, msg)) + } + responses[index] = response + wg.Done() + }(value, index) } - return h.responseCreated(c, "heartbeat created successfully", heartbeat) + wg.Wait() + return h.responseCreated(c, fmt.Sprintf("[%d] heartbeats received successfully", len(responses)), responses) } diff --git a/api/pkg/handlers/lemonsqueezy_handler.go b/api/pkg/handlers/lemonsqueezy_handler.go index d47af5a1..ca9ea0eb 100644 --- a/api/pkg/handlers/lemonsqueezy_handler.go +++ b/api/pkg/handlers/lemonsqueezy_handler.go @@ -44,18 +44,7 @@ func (h *LemonsqueezyHandler) RegisterRoutes(app *fiber.App, middlewares ...fibe router.Post("/event", h.computeRoute(middlewares, h.Event)...) } -// Event consumes a lemonsqueezy event -// @Summary Consume a lemonsqueezy event -// @Description Publish a lemonsqueezy event to the registered listeners -// @Tags Lemonsqueezy -// @Accept json -// @Produce json -// @Success 204 {object} responses.NoContent -// @Failure 400 {object} responses.BadRequest -// @Failure 401 {object} responses.Unauthorized -// @Failure 422 {object} responses.UnprocessableEntity -// @Failure 500 {object} responses.InternalServerError -// @Router /lemonsqueezy/event [post] +// Event handles lemonsqueezy events func (h *LemonsqueezyHandler) Event(c *fiber.Ctx) error { ctx, span, ctxLogger := h.tracer.StartFromFiberCtxWithLogger(c, h.logger) defer span.End() @@ -100,6 +89,13 @@ func (h *LemonsqueezyHandler) handleRequest(ctx context.Context, c *fiber.Ctx) e return stacktrace.Propagate(err, fmt.Sprintf("cannot marshall [%s] to [%T]", c.Body(), request)) } return h.service.HandleSubscriptionExpiredEvent(ctx, c.OriginalURL(), &request) + case "subscription_updated": + var request lemonsqueezy.WebhookRequestSubscription + err := json.Unmarshal(c.Body(), &request) + if err != nil { + return stacktrace.Propagate(err, fmt.Sprintf("cannot marshall [%s] to [%T]", c.Body(), request)) + } + return h.service.HandleSubscriptionUpdatedEvent(ctx, c.OriginalURL(), &request) default: return stacktrace.NewError(fmt.Sprintf("invalid event [%s] received with request [%s]", eventName, c.Body())) } diff --git a/api/pkg/handlers/message_handler.go b/api/pkg/handlers/message_handler.go index a8201c51..935e9ba4 100644 --- a/api/pkg/handlers/message_handler.go +++ b/api/pkg/handlers/message_handler.go @@ -2,7 +2,9 @@ package handlers import ( "fmt" + "strings" "sync" + "sync/atomic" "time" "github.com/NdoleStudio/httpsms/pkg/entities" @@ -47,23 +49,31 @@ func NewMessageHandler( } // RegisterRoutes registers the routes for the MessageHandler -func (h *MessageHandler) RegisterRoutes(router fiber.Router) { - router.Post("/messages/send", h.PostSend) - router.Post("/messages/bulk-send", h.BulkSend) - router.Post("/messages/receive", h.PostReceive) - router.Get("/messages/outstanding", h.GetOutstanding) - router.Get("/messages", h.Index) - router.Post("/messages/:messageID/events", h.PostEvent) +func (h *MessageHandler) RegisterRoutes(router fiber.Router, middlewares ...fiber.Handler) { + router.Post("/v1/messages/send", h.computeRoute(middlewares, h.PostSend)...) + router.Post("/v1/messages/bulk-send", h.computeRoute(middlewares, h.BulkSend)...) + router.Get("/v1/messages", h.computeRoute(middlewares, h.Index)...) + router.Get("/v1/messages/search", h.computeRoute(middlewares, h.Search)...) + router.Get("/v1/messages/:messageID", h.computeRoute(middlewares, h.Get)...) + router.Delete("/v1/messages/:messageID", h.computeRoute(middlewares, h.Delete)...) +} + +// RegisterPhoneAPIKeyRoutes registers the routes for the MessageHandler +func (h *MessageHandler) RegisterPhoneAPIKeyRoutes(router fiber.Router, middlewares ...fiber.Handler) { + router.Post("/v1/messages/:messageID/events", h.computeRoute(middlewares, h.PostEvent)...) + router.Post("/v1/messages/receive", h.computeRoute(middlewares, h.PostReceive)...) + router.Post("/v1/messages/calls/missed", h.computeRoute(middlewares, h.PostCallMissed)...) + router.Get("/v1/messages/outstanding", h.computeRoute(middlewares, h.GetOutstanding)...) } // PostSend a new entities.Message -// @Summary Send a new SMS message -// @Description Add a new SMS message to be sent by the android phone +// @Summary Send an SMS message +// @Description Add a new SMS message to be sent by your Android phone // @Security ApiKeyAuth // @Tags Messages // @Accept json // @Produce json -// @Param payload body requests.MessageSend true "PostSend message request payload" +// @Param payload body requests.MessageSend true "Send message request payload" // @Success 200 {object} responses.MessageResponse // @Failure 400 {object} responses.BadRequest // @Failure 401 {object} responses.Unauthorized @@ -145,13 +155,21 @@ func (h *MessageHandler) BulkSend(c *fiber.Ctx) error { wg := sync.WaitGroup{} params := request.ToMessageSendParams(h.userIDFomContext(c), c.OriginalURL()) responses := make([]*entities.Message, len(params)) + count := atomic.Int64{} for index, message := range params { wg.Add(1) go func(message services.MessageSendParams, index int) { + count.Add(1) + if message.SendAt == nil { + sentAt := time.Now().UTC().Add(time.Duration(index) * time.Second) + message.SendAt = &sentAt + } + response, err := h.service.SendMessage(ctx, message) if err != nil { - msg := fmt.Sprintf("cannot send message with paylod [%s]", c.Body()) + count.Add(-1) + msg := fmt.Sprintf("cannot send message with paylod [%s] at index [%d]", spew.Sdump(message), index) ctxLogger.Error(stacktrace.Propagate(err, msg)) } responses[index] = response @@ -160,7 +178,7 @@ func (h *MessageHandler) BulkSend(c *fiber.Ctx) error { } wg.Wait() - return h.responseOK(c, fmt.Sprintf("[%d] messages processed successfully", len(responses)), responses) + return h.responseOK(c, fmt.Sprintf("%d out of %d messages processed successfully", count.Load(), len(responses)), responses) } // GetOutstanding returns an entities.Message which is still to be sent by the mobile phone @@ -197,11 +215,11 @@ func (h *MessageHandler) GetOutstanding(c *fiber.Ctx) error { return h.responseUnprocessableEntity(c, errors, "validation errors while fetching outstanding messages") } - message, err := h.service.GetOutstanding(ctx, request.ToGetOutstandingParams(c.Path(), h.userIDFomContext(c), timestamp)) + message, err := h.service.GetOutstanding(ctx, request.ToGetOutstandingParams(c.Path(), h.userFromContext(c), timestamp)) if stacktrace.GetCode(err) == repositories.ErrCodeNotFound { - msg := fmt.Sprintf("outstanding message with id [%s] already fetched", request.MessageID) + msg := fmt.Sprintf("Cannot find outstanding message with ID [%s]", request.MessageID) ctxLogger.Warn(stacktrace.Propagate(err, msg)) - return h.responseNotFound(c, "outstanding message already processed") + return h.responseNotFound(c, msg) } if err != nil { @@ -290,6 +308,10 @@ func (h *MessageHandler) PostEvent(c *fiber.Ctx) error { } request.MessageID = c.Params("messageID") + if strings.Contains(request.MessageID, ".") { + return h.responseNoContent(c, "duplicate send event received.") + } + if errors := h.validator.ValidateMessageEvent(ctx, request.Sanitize()); len(errors) != 0 { msg := fmt.Sprintf("validation errors [%s], while storing event [%s] for message [%s]", spew.Sdump(errors), c.Body(), request.MessageID) ctxLogger.Warn(stacktrace.NewError(msg)) @@ -307,6 +329,11 @@ func (h *MessageHandler) PostEvent(c *fiber.Ctx) error { return h.responseInternalServerError(c) } + if !h.authorizePhoneAPIKey(c, message.Owner) { + ctxLogger.Warn(stacktrace.NewError(fmt.Sprintf("user with ID [%s] is not authorized to send event for message with ID [%s]", h.userIDFomContext(c), request.MessageID))) + return h.responsePhoneAPIKeyUnauthorized(c, message.Owner, h.userFromContext(c)) + } + message, err = h.service.StoreEvent(ctx, message, request.ToMessageStoreEventParams(c.OriginalURL())) if err != nil { msg := fmt.Sprintf("cannot store event for message [%s] with paylod [%s]", request.MessageID, c.Body()) @@ -350,10 +377,15 @@ func (h *MessageHandler) PostReceive(c *fiber.Ctx) error { } if msg := h.billingService.IsEntitled(ctx, h.userIDFomContext(c)); msg != nil { - ctxLogger.Warn(stacktrace.NewError(fmt.Sprintf("user with ID [%s] can't receive a message", h.userIDFomContext(c)))) + ctxLogger.Warn(stacktrace.NewError(fmt.Sprintf("user with ID [%s] can't receive a message becasuse they have exceeded the limit", h.userIDFomContext(c)))) return h.responsePaymentRequired(c, *msg) } + if !h.authorizePhoneAPIKey(c, request.To) { + ctxLogger.Warn(stacktrace.NewError(fmt.Sprintf("user with ID [%s] is not authorized to receive message to phone number [%s]", h.userIDFomContext(c), request.To))) + return h.responsePhoneAPIKeyUnauthorized(c, request.To, h.userFromContext(c)) + } + message, err := h.service.ReceiveMessage(ctx, request.ToMessageReceiveParams(h.userIDFomContext(c), c.OriginalURL())) if err != nil { msg := fmt.Sprintf("cannot receive message with paylod [%s]", c.Body()) @@ -363,3 +395,190 @@ func (h *MessageHandler) PostReceive(c *fiber.Ctx) error { return h.responseOK(c, "message received successfully", message) } + +// Delete a message +// @Summary Delete a message from the database. +// @Description Delete a message from the database and removes the message content from the list of threads. +// @Security ApiKeyAuth +// @Tags Messages +// @Accept json +// @Produce json +// @Param messageID path string true "ID of the message" default(32343a19-da5e-4b1b-a767-3298a73703ca) +// @Success 204 {object} responses.NoContent +// @Failure 400 {object} responses.BadRequest +// @Failure 401 {object} responses.Unauthorized +// @Failure 404 {object} responses.NotFound +// @Failure 422 {object} responses.UnprocessableEntity +// @Failure 500 {object} responses.InternalServerError +// @Router /messages/{messageID} [delete] +func (h *MessageHandler) Delete(c *fiber.Ctx) error { + ctx, span := h.tracer.StartFromFiberCtx(c) + defer span.End() + + ctxLogger := h.tracer.CtxLogger(h.logger, span) + + messageID := c.Params("messageID") + if errors := h.validator.ValidateUUID(messageID, "messageID"); len(errors) != 0 { + msg := fmt.Sprintf("validation errors [%s], while deleting a message with ID [%s]", spew.Sdump(errors), messageID) + ctxLogger.Warn(stacktrace.NewError(msg)) + return h.responseUnprocessableEntity(c, errors, "validation errors while storing event") + } + + message, err := h.service.GetMessage(ctx, h.userIDFomContext(c), uuid.MustParse(messageID)) + if stacktrace.GetCode(err) == repositories.ErrCodeNotFound { + return h.responseNotFound(c, fmt.Sprintf("cannot find message with ID [%s]", messageID)) + } + + if err != nil { + msg := fmt.Sprintf("cannot find message with id [%s]", messageID) + ctxLogger.Error(h.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))) + return h.responseInternalServerError(c) + } + + if err = h.service.DeleteMessage(ctx, c.OriginalURL(), message); err != nil { + msg := fmt.Sprintf("cannot delete message with ID [%s] for user with ID [%s]", messageID, message.UserID) + ctxLogger.Error(h.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))) + return h.responseInternalServerError(c) + } + + return h.responseNoContent(c, "message deleted successfully") +} + +// Get a message +// @Summary Get a message from the database. +// @Description Get a message from the database by the message ID. +// @Security ApiKeyAuth +// @Tags Messages +// @Accept json +// @Produce json +// @Param messageID path string true "ID of the message" default(32343a19-da5e-4b1b-a767-3298a73703ca) +// @Success 204 {object} responses.MessageResponse +// @Failure 400 {object} responses.BadRequest +// @Failure 401 {object} responses.Unauthorized +// @Failure 404 {object} responses.NotFound +// @Failure 422 {object} responses.UnprocessableEntity +// @Failure 500 {object} responses.InternalServerError +// @Router /messages/{messageID} [get] +func (h *MessageHandler) Get(c *fiber.Ctx) error { + ctx, span := h.tracer.StartFromFiberCtx(c) + defer span.End() + + ctxLogger := h.tracer.CtxLogger(h.logger, span) + + messageID := c.Params("messageID") + if errors := h.validator.ValidateUUID(messageID, "messageID"); len(errors) != 0 { + msg := fmt.Sprintf("validation errors [%s], while deleting a message with ID [%s]", spew.Sdump(errors), messageID) + ctxLogger.Warn(stacktrace.NewError(msg)) + return h.responseUnprocessableEntity(c, errors, "validation errors while storing event") + } + + message, err := h.service.GetMessage(ctx, h.userIDFomContext(c), uuid.MustParse(messageID)) + if stacktrace.GetCode(err) == repositories.ErrCodeNotFound { + return h.responseNotFound(c, fmt.Sprintf("cannot find message with ID [%s]", messageID)) + } + + if err != nil { + msg := fmt.Sprintf("cannot find message with id [%s]", messageID) + ctxLogger.Error(h.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))) + return h.responseInternalServerError(c) + } + + return h.responseOK(c, "message fetched successfully", message) +} + +// PostCallMissed registers a missed phone call +// @Summary Register a missed call event on the mobile phone +// @Description This endpoint is called by the httpSMS android app to register a missed call event on the mobile phone. +// @Security ApiKeyAuth +// @Tags Messages +// @Accept json +// @Produce json +// @Param payload body requests.MessageCallMissed true "Payload of the missed call event." +// @Success 200 {object} responses.MessageResponse +// @Failure 400 {object} responses.BadRequest +// @Failure 401 {object} responses.Unauthorized +// @Failure 404 {object} responses.NotFound +// @Failure 422 {object} responses.UnprocessableEntity +// @Failure 500 {object} responses.InternalServerError +// @Router /messages/calls/missed [post] +func (h *MessageHandler) PostCallMissed(c *fiber.Ctx) error { + ctx, span := h.tracer.StartFromFiberCtx(c) + defer span.End() + + ctxLogger := h.tracer.CtxLogger(h.logger, span) + + var request requests.MessageCallMissed + if err := c.BodyParser(&request); err != nil { + msg := fmt.Sprintf("cannot marshall [%s] into %T", c.Body(), request) + ctxLogger.Warn(stacktrace.Propagate(err, msg)) + return h.responseBadRequest(c, err) + } + + if errors := h.validator.ValidateCallMissed(ctx, request.Sanitize()); len(errors) != 0 { + msg := fmt.Sprintf("validation errors [%s], for missed call event [%s]", spew.Sdump(errors), c.Body()) + ctxLogger.Warn(stacktrace.NewError(msg)) + return h.responseUnprocessableEntity(c, errors, "validation errors while storing missed call event") + } + + if !h.authorizePhoneAPIKey(c, request.To) { + ctxLogger.Warn(stacktrace.NewError(fmt.Sprintf("user with ID [%s] is not authorized to register missed phone call for phone number [%s]", h.userIDFomContext(c), request.To))) + return h.responsePhoneAPIKeyUnauthorized(c, request.To, h.userFromContext(c)) + } + + message, err := h.service.RegisterMissedCall(ctx, request.ToCallMissedParams(h.userIDFomContext(c), c.OriginalURL())) + if err != nil { + msg := fmt.Sprintf("cannot store missed call event for user [%s] with paylod [%s]", h.userIDFomContext(c), c.Body()) + ctxLogger.Error(h.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))) + return h.responseInternalServerError(c) + } + + return h.responseOK(c, "missed call event stored successfully", message) +} + +// Search returns a filtered list of messages of a user +// @Summary Search all messages of a user +// @Description This returns the list of all messages based on the filter criteria including missed calls +// @Security ApiKeyAuth +// @Tags Messages +// @Accept json +// @Produce json +// @Param token header string true "Cloudflare turnstile token https://www.cloudflare.com/en-gb/application-services/products/turnstile/" +// @Param owners query string true "the owner's phone numbers" default(+18005550199,+18005550100) +// @Param skip query int false "number of messages to skip" minimum(0) +// @Param query query string false "filter messages containing query" +// @Param limit query int false "number of messages to return" minimum(1) maximum(200) +// @Success 200 {object} responses.MessagesResponse +// @Failure 400 {object} responses.BadRequest +// @Failure 401 {object} responses.Unauthorized +// @Failure 422 {object} responses.UnprocessableEntity +// @Failure 500 {object} responses.InternalServerError +// @Router /messages/search [get] +func (h *MessageHandler) Search(c *fiber.Ctx) error { + ctx, span, ctxLogger := h.tracer.StartFromFiberCtxWithLogger(c, h.logger) + defer span.End() + + var request requests.MessageSearch + if err := c.QueryParser(&request); err != nil { + msg := fmt.Sprintf("cannot marshall params in [%s] into [%T]", c.OriginalURL(), request) + ctxLogger.Warn(stacktrace.Propagate(err, msg)) + return h.responseBadRequest(c, err) + } + + request.IPAddress = c.IP() + request.Token = c.Get("token") + + if errors := h.validator.ValidateMessageSearch(ctx, request.Sanitize()); len(errors) != 0 { + msg := fmt.Sprintf("validation errors [%s], while searching messages [%+#v]", spew.Sdump(errors), request) + ctxLogger.Warn(stacktrace.NewError(msg)) + return h.responseUnprocessableEntity(c, errors, "validation errors while searching messages") + } + + messages, err := h.service.SearchMessages(ctx, request.ToSearchParams(h.userIDFomContext(c))) + if err != nil { + msg := fmt.Sprintf("cannot search messages with params [%+#v]", request) + ctxLogger.Error(stacktrace.Propagate(err, msg)) + return h.responseInternalServerError(c) + } + + return h.responseOK(c, fmt.Sprintf("found %d %s", len(messages), h.pluralize("message", len(messages))), messages) +} diff --git a/api/pkg/handlers/message_thread_handler.go b/api/pkg/handlers/message_thread_handler.go index 4e46bea1..fc83d47c 100644 --- a/api/pkg/handlers/message_thread_handler.go +++ b/api/pkg/handlers/message_thread_handler.go @@ -3,6 +3,9 @@ package handlers import ( "fmt" + "github.com/NdoleStudio/httpsms/pkg/repositories" + "github.com/google/uuid" + "github.com/NdoleStudio/httpsms/pkg/requests" "github.com/NdoleStudio/httpsms/pkg/services" "github.com/NdoleStudio/httpsms/pkg/telemetry" @@ -37,16 +40,17 @@ func NewMessageThreadHandler( } // RegisterRoutes registers the routes for the MessageHandler -func (h *MessageThreadHandler) RegisterRoutes(router fiber.Router) { - router.Get("/message-threads", h.Index) - router.Put("/message-threads/:messageThreadID", h.Update) +func (h *MessageThreadHandler) RegisterRoutes(router fiber.Router, middlewares ...fiber.Handler) { + router.Get("/v1/message-threads", h.computeRoute(middlewares, h.Index)...) + router.Put("/v1/message-threads/:messageThreadID", h.computeRoute(middlewares, h.Update)...) + router.Delete("/v1/message-threads/:messageThreadID", h.computeRoute(middlewares, h.Delete)...) } // Index returns message threads for a phone number // @Summary Get message threads for a phone number // @Description Get list of contacts which a phone number has communicated with (threads). It will be sorted by timestamp in descending order. // @Security ApiKeyAuth -// @Tags Channel Threads +// @Tags MessageThreads // @Accept json // @Produce json // @Param owner query string true "owner phone number" default(+18005550199) @@ -94,7 +98,7 @@ func (h *MessageThreadHandler) Index(c *fiber.Ctx) error { // @Summary Update a message thread // @Description Updates the details of a message thread // @Security ApiKeyAuth -// @Tags Channel Threads +// @Tags MessageThreads // @Accept json // @Produce json // @Param messageThreadID path string true "ID of the message thread" default(32343a19-da5e-4b1b-a767-3298a73703ca) @@ -106,11 +110,9 @@ func (h *MessageThreadHandler) Index(c *fiber.Ctx) error { // @Failure 500 {object} responses.InternalServerError // @Router /message-threads/{messageThreadID} [put] func (h *MessageThreadHandler) Update(c *fiber.Ctx) error { - ctx, span := h.tracer.StartFromFiberCtx(c) + ctx, span, ctxLogger := h.tracer.StartFromFiberCtxWithLogger(c, h.logger) defer span.End() - ctxLogger := h.tracer.CtxLogger(h.logger, span) - var request requests.MessageThreadUpdate if err := c.BodyParser(&request); err != nil { msg := fmt.Sprintf("cannot marshall params [%s] into %T", c.OriginalURL(), request) @@ -134,3 +136,49 @@ func (h *MessageThreadHandler) Update(c *fiber.Ctx) error { return h.responseOK(c, "message thread updated successfully", thread) } + +// Delete a message thread +// @Summary Delete a message thread from the database. +// @Description Delete a message thread from the database and also deletes all the messages in the thread. +// @Security ApiKeyAuth +// @Tags MessageThreads +// @Accept json +// @Produce json +// @Param messageThreadID path string true "ID of the message thread" default(32343a19-da5e-4b1b-a767-3298a73703ca) +// @Success 204 {object} responses.NoContent +// @Failure 400 {object} responses.BadRequest +// @Failure 401 {object} responses.Unauthorized +// @Failure 404 {object} responses.NotFound +// @Failure 422 {object} responses.UnprocessableEntity +// @Failure 500 {object} responses.InternalServerError +// @Router /message-threads/{messageThreadID} [delete] +func (h *MessageThreadHandler) Delete(c *fiber.Ctx) error { + ctx, span, ctxLogger := h.tracer.StartFromFiberCtxWithLogger(c, h.logger) + defer span.End() + + messageThreadID := c.Params("messageThreadID") + if errors := h.validator.ValidateUUID(messageThreadID, "messageThreadID"); len(errors) != 0 { + msg := fmt.Sprintf("validation errors [%s], while deleting a thread thread with ID [%s]", spew.Sdump(errors), messageThreadID) + ctxLogger.Warn(stacktrace.NewError(msg)) + return h.responseUnprocessableEntity(c, errors, "validation errors while deleting a thread thread") + } + + thread, err := h.service.GetThread(ctx, h.userIDFomContext(c), uuid.MustParse(messageThreadID)) + if stacktrace.GetCode(err) == repositories.ErrCodeNotFound { + return h.responseNotFound(c, fmt.Sprintf("cannot find thread thread with ID [%s]", messageThreadID)) + } + + if err != nil { + msg := fmt.Sprintf("cannot find thread thread with id [%s]", messageThreadID) + ctxLogger.Error(h.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))) + return h.responseInternalServerError(c) + } + + if err = h.service.DeleteThread(ctx, c.OriginalURL(), thread); err != nil { + msg := fmt.Sprintf("cannot delete thread thread with ID [%s] for user with ID [%s]", messageThreadID, thread.UserID) + ctxLogger.Error(h.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))) + return h.responseInternalServerError(c) + } + + return h.responseNoContent(c, "thread thread deleted successfully") +} diff --git a/api/pkg/handlers/phone_api_key_handler.go b/api/pkg/handlers/phone_api_key_handler.go new file mode 100644 index 00000000..c10df513 --- /dev/null +++ b/api/pkg/handlers/phone_api_key_handler.go @@ -0,0 +1,210 @@ +package handlers + +import ( + "fmt" + + "github.com/NdoleStudio/httpsms/pkg/repositories" + "github.com/NdoleStudio/httpsms/pkg/requests" + "github.com/NdoleStudio/httpsms/pkg/services" + "github.com/NdoleStudio/httpsms/pkg/telemetry" + "github.com/NdoleStudio/httpsms/pkg/validators" + "github.com/davecgh/go-spew/spew" + "github.com/gofiber/fiber/v2" + "github.com/google/uuid" + "github.com/palantir/stacktrace" +) + +// PhoneAPIKeyHandler handles phone API key http requests +type PhoneAPIKeyHandler struct { + handler + logger telemetry.Logger + tracer telemetry.Tracer + validator *validators.PhoneAPIKeyHandlerValidator + service *services.PhoneAPIKeyService +} + +// NewPhoneAPIKeyHandler creates a new PhoneAPIKeyHandler +func NewPhoneAPIKeyHandler( + logger telemetry.Logger, + tracer telemetry.Tracer, + validator *validators.PhoneAPIKeyHandlerValidator, + service *services.PhoneAPIKeyService, +) *PhoneAPIKeyHandler { + return &PhoneAPIKeyHandler{ + logger: logger.WithService(fmt.Sprintf("%T", &PhoneAPIKeyHandler{})), + tracer: tracer, + validator: validator, + service: service, + } +} + +// RegisterRoutes registers the routes for the PhoneAPIKeyHandler +func (h *PhoneAPIKeyHandler) RegisterRoutes(app *fiber.App, middlewares ...fiber.Handler) { + router := app.Group("/v1/phone-api-keys/") + router.Get("/", h.computeRoute(middlewares, h.index)...) + router.Post("/", h.computeRoute(middlewares, h.store)...) + router.Delete("/:phoneAPIKeyID", h.computeRoute(middlewares, h.delete)...) + router.Delete("/:phoneAPIKeyID/phones/:phoneID", h.computeRoute(middlewares, h.deletePhone)...) +} + +// @Summary Get the phone API keys of a user +// @Description Get list phone API keys which a user has registered on the httpSMS application +// @Security ApiKeyAuth +// @Tags PhoneAPIKeys +// @Accept json +// @Produce json +// @Param skip query int false "number of phone api keys to skip" minimum(0) +// @Param query query string false "filter phone api keys with name containing query" +// @Param limit query int false "number of phone api keys to return" minimum(1) maximum(100) +// @Success 200 {object} responses.PhoneAPIKeysResponse +// @Failure 400 {object} responses.BadRequest +// @Failure 401 {object} responses.Unauthorized +// @Failure 422 {object} responses.UnprocessableEntity +// @Failure 500 {object} responses.InternalServerError +// @Router /phone-api-keys [get] +func (h *PhoneAPIKeyHandler) index(c *fiber.Ctx) error { + ctx, span, ctxLogger := h.tracer.StartFromFiberCtxWithLogger(c, h.logger) + defer span.End() + + var request requests.PhoneAPIKeyIndex + if err := c.QueryParser(&request); err != nil { + msg := fmt.Sprintf("cannot marshall params [%s] into %T", c.OriginalURL(), request) + ctxLogger.Warn(stacktrace.Propagate(err, msg)) + return h.responseBadRequest(c, err) + } + + if errors := h.validator.ValidateIndex(ctx, request.Sanitize()); len(errors) != 0 { + msg := fmt.Sprintf("validation errors [%s], while fetching phone API keys [%+#v]", spew.Sdump(errors), request) + ctxLogger.Warn(stacktrace.NewError(msg)) + return h.responseUnprocessableEntity(c, errors, "validation errors while fetching phone API keys") + } + + apiKeys, err := h.service.Index(ctx, h.userIDFomContext(c), request.ToIndexParams()) + if err != nil { + msg := fmt.Sprintf("cannot index phone API keys with params [%+#v]", request) + ctxLogger.Error(stacktrace.Propagate(err, msg)) + return h.responseInternalServerError(c) + } + + return h.responseOK(c, fmt.Sprintf("fetched %d phone API %s", len(apiKeys), h.pluralize("key", len(apiKeys))), apiKeys) +} + +// @Summary Store phone API key +// @Description Creates a new phone API key which can be used to log in to the httpSMS app on your Android phone +// @Security ApiKeyAuth +// @Tags PhoneAPIKeys +// @Accept json +// @Produce json +// @Param payload body requests.PhoneAPIKeyStoreRequest true "Payload of new phone API key." +// @Success 200 {object} responses.PhoneAPIKeyResponse +// @Failure 400 {object} responses.BadRequest +// @Failure 401 {object} responses.Unauthorized +// @Failure 422 {object} responses.UnprocessableEntity +// @Failure 500 {object} responses.InternalServerError +// @Router /phone-api-keys [post] +func (h *PhoneAPIKeyHandler) store(c *fiber.Ctx) error { + ctx, span, ctxLogger := h.tracer.StartFromFiberCtxWithLogger(c, h.logger) + defer span.End() + + var request requests.PhoneAPIKeyStoreRequest + if err := c.BodyParser(&request); err != nil { + msg := fmt.Sprintf("cannot marshall params [%s] into %T", c.OriginalURL(), request) + ctxLogger.Warn(stacktrace.Propagate(err, msg)) + return h.responseBadRequest(c, err) + } + + if errors := h.validator.ValidateStore(ctx, request.Sanitize()); len(errors) != 0 { + msg := fmt.Sprintf("validation errors [%s], while updating phones [%+#v]", spew.Sdump(errors), request) + ctxLogger.Warn(stacktrace.NewError(msg)) + return h.responseUnprocessableEntity(c, errors, "validation errors while updating phones") + } + + phoneAPIKey, err := h.service.Create(ctx, h.userFromContext(c), request.Name) + if err != nil { + msg := fmt.Sprintf("cannot update phones with params [%+#v]", request) + ctxLogger.Error(stacktrace.Propagate(err, msg)) + return h.responseInternalServerError(c) + } + + return h.responseOK(c, "phone API key created successfully", phoneAPIKey) +} + +// @Summary Delete a phone API key from the database. +// @Description Delete a phone API Key from the database and cannot be used for authentication anymore. +// @Security ApiKeyAuth +// @Tags PhoneAPIKeys +// @Accept json +// @Produce json +// @Param phoneAPIKeyID path string true "ID of the phone API key" default(32343a19-da5e-4b1b-a767-3298a73703ca) +// @Success 204 {object} responses.NoContent +// @Failure 400 {object} responses.BadRequest +// @Failure 401 {object} responses.Unauthorized +// @Failure 404 {object} responses.NotFound +// @Failure 422 {object} responses.UnprocessableEntity +// @Failure 500 {object} responses.InternalServerError +// @Router /phone-api-keys/{phoneAPIKeyID} [delete] +func (h *PhoneAPIKeyHandler) delete(c *fiber.Ctx) error { + ctx, span, ctxLogger := h.tracer.StartFromFiberCtxWithLogger(c, h.logger) + defer span.End() + + phoneAPIKeyID := c.Params("phoneAPIKeyID") + if errors := h.validator.ValidateUUID(phoneAPIKeyID, "phoneAPIKeyID"); len(errors) != 0 { + msg := fmt.Sprintf("validation errors [%s], while deleting a phone API key with ID [%s]", spew.Sdump(errors), phoneAPIKeyID) + ctxLogger.Warn(stacktrace.NewError(msg)) + return h.responseUnprocessableEntity(c, errors, "validation errors while storing event") + } + + err := h.service.Delete(ctx, h.userIDFomContext(c), uuid.MustParse(phoneAPIKeyID)) + if stacktrace.GetCode(err) == repositories.ErrCodeNotFound { + return h.responseNotFound(c, fmt.Sprintf("cannot find phone API key with ID [%s]", phoneAPIKeyID)) + } + + if err != nil { + msg := fmt.Sprintf("cannot delete phone API key with ID [%s] for user with ID [%s]", phoneAPIKeyID, h.userIDFomContext(c)) + ctxLogger.Error(h.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))) + return h.responseInternalServerError(c) + } + + return h.responseNoContent(c, "phone API key deleted successfully") +} + +// @Summary Remove the association of a phone from the phone API key. +// @Description You will need to login again to the httpSMS app on your Android phone with a new phone API key. +// @Security ApiKeyAuth +// @Tags PhoneAPIKeys +// @Accept json +// @Produce json +// @Param phoneAPIKeyID path string true "ID of the phone API key" default(32343a19-da5e-4b1b-a767-3298a73703ca) +// @Param phoneID path string true "ID of the phone" default(32343a19-da5e-4b1b-a767-3298a73703ca) +// @Success 204 {object} responses.NoContent +// @Failure 400 {object} responses.BadRequest +// @Failure 401 {object} responses.Unauthorized +// @Failure 404 {object} responses.NotFound +// @Failure 422 {object} responses.UnprocessableEntity +// @Failure 500 {object} responses.InternalServerError +// @Router /phone-api-keys/{phoneAPIKeyID}/phones/{phoneID} [delete] +func (h *PhoneAPIKeyHandler) deletePhone(c *fiber.Ctx) error { + ctx, span, ctxLogger := h.tracer.StartFromFiberCtxWithLogger(c, h.logger) + defer span.End() + + phoneAPIKeyID := c.Params("phoneAPIKeyID") + phoneID := c.Params("phoneID") + if errors := h.mergeErrors(h.validator.ValidateUUID(phoneAPIKeyID, "phoneAPIKeyID"), h.validator.ValidateUUID(phoneID, "phoneID")); len(errors) != 0 { + msg := fmt.Sprintf("validation errors [%s], while deleting a phone API key with ID [%s]", spew.Sdump(errors), phoneAPIKeyID) + ctxLogger.Warn(stacktrace.NewError(msg)) + return h.responseUnprocessableEntity(c, errors, "validation errors while storing event") + } + + err := h.service.RemovePhone(ctx, h.userIDFomContext(c), uuid.MustParse(phoneAPIKeyID), uuid.MustParse(phoneID)) + if stacktrace.GetCode(err) == repositories.ErrCodeNotFound { + return h.responseNotFound(c, fmt.Sprintf("cannot find phone with ID [%s] which is associated with phone API key with ID [%s]", phoneID, phoneAPIKeyID)) + } + + if err != nil { + msg := fmt.Sprintf("cannot remove phone with ID [%s] from phone API key with ID [%s] for user with ID [%s]", phoneID, phoneAPIKeyID, h.userIDFomContext(c)) + ctxLogger.Error(h.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))) + return h.responseInternalServerError(c) + } + + return h.responseNoContent(c, "phone has been dissociated from phone API key successfully") +} diff --git a/api/pkg/handlers/phone_api_key_handler_test.go b/api/pkg/handlers/phone_api_key_handler_test.go new file mode 100644 index 00000000..a2bc0a4b --- /dev/null +++ b/api/pkg/handlers/phone_api_key_handler_test.go @@ -0,0 +1,116 @@ +package handlers + +import ( + "context" + "slices" + "strings" + "testing" + + "github.com/NdoleStudio/httpsms/pkg/entities" + "github.com/NdoleStudio/httpsms/pkg/requests" + "github.com/NdoleStudio/httpsms/pkg/responses" + "github.com/jaswdr/faker/v2" + "github.com/stretchr/testify/assert" +) + +func TestPhoneAPIKeyHandler_store(t *testing.T) { + // Arrange + fake := faker.New() + payload := requests.PhoneAPIKeyStoreRequest{ + Name: fake.RandomStringWithLength(20), + } + response := new(responses.PhoneAPIKeyResponse) + + // Act + err := testClient(). + Post(). + Path("/v1/phone-api-keys"). + BodyJSON(payload). + ToJSON(response). + Fetch(context.Background()) + + // Assert + assert.Nil(t, err) + assert.NotEmpty(t, response.Data.ID) + assert.True(t, strings.HasPrefix(response.Data.APIKey, "pk_")) + assert.Equal(t, payload.Name, response.Data.Name) + assert.True(t, len(response.Data.PhoneNumbers) == 0) + assert.True(t, len(response.Data.PhoneIDs) == 0) + + // Teardown + _ = testClient(). + Delete(). + Path("/v1/phone-api-keys/" + response.Data.ID.String()). + Fetch(context.Background()) +} + +func TestPhoneAPIKeyHandler_delete(t *testing.T) { + // Arrange + fake := faker.New() + payload := requests.PhoneAPIKeyStoreRequest{ + Name: fake.RandomStringWithLength(20), + } + + // Act + response := new(responses.PhoneAPIKeyResponse) + _ = testClient(). + Post(). + Path("/v1/phone-api-keys"). + BodyJSON(payload). + ToJSON(response). + Fetch(context.Background()) + + err := testClient(). + Delete(). + Path("/v1/phone-api-keys/" + response.Data.ID.String()). + Fetch(context.Background()) + + // Assert + assert.Nil(t, err) + + keys := new(responses.PhoneAPIKeysResponse) + _ = testClient(). + Path("/v1/phone-api-keys"). + ToJSON(response). + Fetch(context.Background()) + + assert.Equal(t, -1, slices.IndexFunc(keys.Data, func(key *entities.PhoneAPIKey) bool { + return key.ID == response.Data.ID + })) +} + +func TestPhoneAPIKeyHandler_index(t *testing.T) { + // Arrange + fake := faker.New() + createResponse := new(responses.PhoneAPIKeyResponse) + response := new(responses.PhoneAPIKeysResponse) + payload := requests.PhoneAPIKeyStoreRequest{ + Name: fake.RandomStringWithLength(20), + } + + // Act + _ = testClient(). + Post(). + Path("/v1/phone-api-keys"). + BodyJSON(payload). + ToJSON(createResponse). + Fetch(context.Background()) + + err := testClient(). + Path("/v1/phone-api-keys"). + ToJSON(response). + Fetch(context.Background()) + + // Assert + assert.Nil(t, err) + assert.NotEmpty(t, response.Data) + assert.NotEqual(t, -1, slices.IndexFunc(response.Data, func(key *entities.PhoneAPIKey) bool { + return key.ID == createResponse.Data.ID + })) + + // Teardown + _ = testClient(). + Delete(). + Path("/v1/phone-api-keys/" + createResponse.Data.ID.String()). + Fetch(context.Background()) +} diff --git a/api/pkg/handlers/phone_handler.go b/api/pkg/handlers/phone_handler.go index b64c9d0f..9e5cbe1c 100644 --- a/api/pkg/handlers/phone_handler.go +++ b/api/pkg/handlers/phone_handler.go @@ -38,10 +38,15 @@ func NewPhoneHandler( } // RegisterRoutes registers the routes for the PhoneHandler -func (h *PhoneHandler) RegisterRoutes(router fiber.Router) { - router.Get("/phones", h.Index) - router.Put("/phones", h.Upsert) - router.Delete("/phones/:phoneID", h.Delete) +func (h *PhoneHandler) RegisterRoutes(router fiber.Router, middlewares ...fiber.Handler) { + router.Get("/v1/phones", h.computeRoute(middlewares, h.Index)...) + router.Put("/v1/phones", h.computeRoute(middlewares, h.Upsert)...) + router.Delete("/v1/phones/:phoneID", h.computeRoute(middlewares, h.Delete)...) +} + +// RegisterPhoneAPIKeyRoutes registers the routes for the PhoneHandler +func (h *PhoneHandler) RegisterPhoneAPIKeyRoutes(router fiber.Router, middlewares ...fiber.Handler) { + router.Put("/v1/phones/fcm-token", h.computeRoute(middlewares, h.UpsertFCMToken)...) } // Index returns the phones of a user @@ -168,3 +173,46 @@ func (h *PhoneHandler) Delete(c *fiber.Ctx) error { return h.responseOK(c, "phone deleted successfully", nil) } + +// UpsertFCMToken upserts the FCM token of a phone +// @Summary Upserts the FCM token of a phone +// @Description Updates the FCM token of a phone. If the phone with this number does not exist, a new one will be created. Think of this method like an 'upsert' +// @Security ApiKeyAuth +// @Tags Phones +// @Accept json +// @Produce json +// @Param payload body requests.PhoneFCMToken true "Payload of new FCM token." +// @Success 200 {object} responses.PhoneResponse +// @Failure 400 {object} responses.BadRequest +// @Failure 401 {object} responses.Unauthorized +// @Failure 422 {object} responses.UnprocessableEntity +// @Failure 500 {object} responses.InternalServerError +// @Router /phones/fcm-token [put] +func (h *PhoneHandler) UpsertFCMToken(c *fiber.Ctx) error { + ctx, span := h.tracer.StartFromFiberCtx(c) + defer span.End() + + ctxLogger := h.tracer.CtxLogger(h.logger, span) + + var request requests.PhoneFCMToken + if err := c.BodyParser(&request); err != nil { + msg := fmt.Sprintf("cannot marshall params [%s] into %T", c.OriginalURL(), request) + ctxLogger.Warn(stacktrace.Propagate(err, msg)) + return h.responseBadRequest(c, err) + } + + if errors := h.validator.ValidateFCMToken(ctx, request.Sanitize()); len(errors) != 0 { + msg := fmt.Sprintf("validation errors [%s], while updating phones [%+#v]", spew.Sdump(errors), request) + ctxLogger.Warn(stacktrace.NewError(msg)) + return h.responseUnprocessableEntity(c, errors, "validation errors while updating phones") + } + + phone, err := h.service.UpsertFCMToken(ctx, request.ToPhoneFCMTokenParams(h.userFromContext(c), c.OriginalURL())) + if err != nil { + msg := fmt.Sprintf("cannot delete phones with params [%+#v]", request) + ctxLogger.Error(stacktrace.Propagate(err, msg)) + return h.responseInternalServerError(c) + } + + return h.responseOK(c, "FCM token updated successfully", phone) +} diff --git a/api/pkg/handlers/user_handler.go b/api/pkg/handlers/user_handler.go index 163c671f..d63046dc 100644 --- a/api/pkg/handlers/user_handler.go +++ b/api/pkg/handlers/user_handler.go @@ -38,12 +38,16 @@ func NewUserHandler( } // RegisterRoutes registers the routes for the MessageHandler -func (h *UserHandler) RegisterRoutes(router fiber.Router) { - router.Get("/users/me", h.Show) - router.Put("/users/me", h.Update) - router.Put("/users/:userID/notifications", h.UpdateNotifications) - router.Get("/users/subscription-update-url", h.subscriptionUpdateURL) - router.Delete("/users/subscription", h.cancelSubscription) +func (h *UserHandler) RegisterRoutes(router fiber.Router, middlewares ...fiber.Handler) { + router.Get("/v1/users/me", h.computeRoute(middlewares, h.Show)...) + router.Put("/v1/users/me", h.computeRoute(middlewares, h.Update)...) + router.Delete("/v1/users/me", h.computeRoute(middlewares, h.Delete)...) + router.Delete("/v1/users/:userID/api-keys", h.computeRoute(middlewares, h.DeleteAPIKey)...) + router.Put("/v1/users/:userID/notifications", h.computeRoute(middlewares, h.UpdateNotifications)...) + router.Get("/v1/users/subscription-update-url", h.computeRoute(middlewares, h.subscriptionUpdateURL)...) + router.Delete("/v1/users/subscription", h.computeRoute(middlewares, h.cancelSubscription)...) + router.Get("/v1/users/subscription/payments", h.computeRoute(middlewares, h.subscriptionPayments)...) + router.Post("/v1/users/subscription/invoices/:subscriptionInvoiceID", h.computeRoute(middlewares, h.subscriptionInvoice)...) } // Show returns an entities.User @@ -60,14 +64,11 @@ func (h *UserHandler) RegisterRoutes(router fiber.Router) { // @Failure 500 {object} responses.InternalServerError // @Router /users/me [get] func (h *UserHandler) Show(c *fiber.Ctx) error { - ctx, span := h.tracer.StartFromFiberCtx(c) + ctx, span, ctxLogger := h.tracer.StartFromFiberCtxWithLogger(c, h.logger) defer span.End() - ctxLogger := h.tracer.CtxLogger(h.logger, span) - authUser := h.userFromContext(c) - - user, err := h.service.Get(ctx, authUser) + user, err := h.service.Get(ctx, c.OriginalURL(), authUser) if err != nil { msg := fmt.Sprintf("cannot get user with ID [%s]", authUser.ID) ctxLogger.Error(stacktrace.Propagate(err, msg)) @@ -110,7 +111,7 @@ func (h *UserHandler) Update(c *fiber.Ctx) error { return h.responseUnprocessableEntity(c, errors, "validation errors while updating user") } - user, err := h.service.Update(ctx, h.userFromContext(c), request.ToUpdateParams()) + user, err := h.service.Update(ctx, c.OriginalURL(), h.userFromContext(c), request.ToUpdateParams()) if err != nil { msg := fmt.Sprintf("cannot update user with params [%+#v]", request) ctxLogger.Error(stacktrace.Propagate(err, msg)) @@ -120,6 +121,30 @@ func (h *UserHandler) Update(c *fiber.Ctx) error { return h.responseOK(c, "user updated successfully", user) } +// Delete an entities.User +// @Summary Delete a user +// @Description Deletes the currently authenticated user together with all their data. +// @Security ApiKeyAuth +// @Tags Users +// @Accept json +// @Produce json +// @Success 201 {object} responses.NoContent +// @Failure 401 {object} responses.Unauthorized +// @Failure 500 {object} responses.InternalServerError +// @Router /users/me [delete] +func (h *UserHandler) Delete(c *fiber.Ctx) error { + ctx, span, ctxLogger := h.tracer.StartFromFiberCtxWithLogger(c, h.logger) + defer span.End() + + if err := h.service.Delete(ctx, c.OriginalURL(), h.userIDFomContext(c)); err != nil { + msg := fmt.Sprintf("cannot delete user user with ID [%s]", h.userIDFomContext(c)) + ctxLogger.Error(stacktrace.Propagate(err, msg)) + return h.responseInternalServerError(c) + } + + return h.responseNoContent(c, "user deleted successfully") +} + // UpdateNotifications an entities.User // @Summary Update notification settings // @Description Update the email notification settings for a user @@ -136,11 +161,9 @@ func (h *UserHandler) Update(c *fiber.Ctx) error { // @Failure 500 {object} responses.InternalServerError // @Router /users/{userID}/notifications [put] func (h *UserHandler) UpdateNotifications(c *fiber.Ctx) error { - ctx, span := h.tracer.StartFromFiberCtx(c) + ctx, span, ctxLogger := h.tracer.StartFromFiberCtxWithLogger(c, h.logger) defer span.End() - ctxLogger := h.tracer.CtxLogger(h.logger, span) - var request requests.UserNotificationUpdate if err := c.BodyParser(&request); err != nil { msg := fmt.Sprintf("cannot marshall params [%s] into %T", c.OriginalURL(), request) @@ -215,3 +238,110 @@ func (h *UserHandler) cancelSubscription(c *fiber.Ctx) error { return h.responseNoContent(c, "Subscription cancelled successfully") } + +// DeleteAPIKey rotates the API Key for a user +// @Summary Rotate the user's API Key +// @Description Rotate the user's API key in case the current API Key is compromised +// @Security ApiKeyAuth +// @Tags Users +// @Accept json +// @Produce json +// @Param userID path string true "ID of the user to update" default(32343a19-da5e-4b1b-a767-3298a73703ca) +// @Success 200 {object} responses.UserResponse +// @Failure 400 {object} responses.BadRequest +// @Failure 401 {object} responses.Unauthorized +// @Failure 422 {object} responses.UnprocessableEntity +// @Failure 500 {object} responses.InternalServerError +// @Router /users/{userID}/api-keys [delete] +func (h *UserHandler) DeleteAPIKey(c *fiber.Ctx) error { + ctx, span := h.tracer.StartFromFiberCtx(c) + defer span.End() + + ctxLogger := h.tracer.CtxLogger(h.logger, span) + + if c.Params("userID") != string(h.userIDFomContext(c)) { + return h.responseUnauthorized(c) + } + + user, err := h.service.RotateAPIKey(ctx, c.OriginalURL(), h.userIDFomContext(c)) + if err != nil { + msg := fmt.Sprintf("cannot rotate the api key for [%T] with ID [%s]", user, h.userIDFomContext(c)) + ctxLogger.Error(h.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))) + return h.responseInternalServerError(c) + } + + return h.responseOK(c, "API Key rotated successfully", user) +} + +// subscriptionPayments returns the last 10 payments of the currently authenticated user +// @Summary Get the last 10 subscription payments. +// @Description Subscription payments are generated throughout the lifecycle of a subscription, typically there is one at the time of purchase and then one for each renewal. +// @Security ApiKeyAuth +// @Tags Users +// @Accept json +// @Produce json +// @Success 200 {object} responses.UserSubscriptionPaymentsResponse +// @Failure 400 {object} responses.BadRequest +// @Failure 401 {object} responses.Unauthorized +// @Failure 422 {object} responses.UnprocessableEntity +// @Failure 500 {object} responses.InternalServerError +// @Router /users/subscription/payments [get] +func (h *UserHandler) subscriptionPayments(c *fiber.Ctx) error { + ctx, span, ctxLogger := h.tracer.StartFromFiberCtxWithLogger(c, h.logger) + defer span.End() + + invoices, err := h.service.GetSubscriptionPayments(ctx, h.userIDFomContext(c)) + if err != nil { + msg := fmt.Sprintf("cannot get current subscription invoices for user [%s]", h.userFromContext(c)) + ctxLogger.Error(stacktrace.Propagate(err, msg)) + return h.responseInternalServerError(c) + } + + return h.responseOK(c, "fetched subscription invoices billing usage", invoices) +} + +// subscriptionInvoice generates an invoice for a given subscription invoice ID +// @Summary Generate a subscription payment invoice +// @Description Generates a new invoice PDF file for the given subscription payment with given parameters. +// @Security ApiKeyAuth +// @Tags Users +// @Accept json +// @Produce application/pdf +// @Param payload body requests.UserPaymentInvoice true "Generate subscription payment invoice parameters" +// @Param subscriptionInvoiceID path string true "ID of the subscription invoice to generate the PDF for" +// @Success 200 {file} file +// @Failure 400 {object} responses.BadRequest +// @Failure 401 {object} responses.Unauthorized +// @Failure 422 {object} responses.UnprocessableEntity +// @Failure 500 {object} responses.InternalServerError +// @Router /users/subscription/invoices/{subscriptionInvoiceID} [post] +func (h *UserHandler) subscriptionInvoice(c *fiber.Ctx) error { + ctx, span, ctxLogger := h.tracer.StartFromFiberCtxWithLogger(c, h.logger) + defer span.End() + + var request requests.UserPaymentInvoice + if err := c.BodyParser(&request); err != nil { + msg := fmt.Sprintf("cannot marshall params [%s] into %T", c.Body(), request) + ctxLogger.Warn(stacktrace.Propagate(err, msg)) + return h.responseBadRequest(c, err) + } + + request.SubscriptionInvoiceID = c.Params("subscriptionInvoiceID") + if errors := h.validator.ValidatePaymentInvoice(ctx, h.userIDFomContext(c), request.Sanitize()); len(errors) != 0 { + msg := fmt.Sprintf("validation errors [%s], while validating subscription payment invoice request [%s]", spew.Sdump(errors), c.Body()) + ctxLogger.Warn(stacktrace.NewError(msg)) + return h.responseUnprocessableEntity(c, errors, "validation errors while generating payment invoice") + } + + data, err := h.service.GenerateReceipt(ctx, request.UserInvoiceGenerateParams(h.userIDFomContext(c))) + if err != nil { + msg := fmt.Sprintf("cannot generate receipt for invoice ID [%s] and user [%s]", request.SubscriptionInvoiceID, h.userFromContext(c)) + ctxLogger.Error(stacktrace.Propagate(err, msg)) + return h.responseInternalServerError(c) + } + + c.Set(fiber.HeaderContentType, "application/pdf") + c.Set(fiber.HeaderContentDisposition, fmt.Sprintf("attachment; filename=\"httpsms.com - %s.pdf\"", request.SubscriptionInvoiceID)) + + return c.SendStream(data) +} diff --git a/api/pkg/handlers/webhook_handler.go b/api/pkg/handlers/webhook_handler.go index 7557d85e..54df0f68 100644 --- a/api/pkg/handlers/webhook_handler.go +++ b/api/pkg/handlers/webhook_handler.go @@ -41,12 +41,11 @@ func NewWebhookHandler( } // RegisterRoutes registers the routes for the WebhookHandler -func (h *WebhookHandler) RegisterRoutes(app *fiber.App, middlewares ...fiber.Handler) { - router := app.Group("/v1/webhooks") - router.Get("/", h.computeRoute(middlewares, h.Index)...) - router.Post("/", h.computeRoute(middlewares, h.Store)...) - router.Put("/:webhookID", h.computeRoute(middlewares, h.Update)...) - router.Delete("/:webhookID", h.computeRoute(middlewares, h.Delete)...) +func (h *WebhookHandler) RegisterRoutes(router fiber.Router, middlewares ...fiber.Handler) { + router.Get("/v1/webhooks", h.computeRoute(middlewares, h.Index)...) + router.Post("/v1/webhooks", h.computeRoute(middlewares, h.Store)...) + router.Put("/v1/webhooks/:webhookID", h.computeRoute(middlewares, h.Update)...) + router.Delete("/v1/webhooks/:webhookID", h.computeRoute(middlewares, h.Delete)...) } // Index returns the webhooks of a user @@ -111,7 +110,7 @@ func (h *WebhookHandler) Delete(c *fiber.Ctx) error { defer span.End() webhookID := c.Params("webhookID") - if errors := h.validator.ValidateUUID(ctx, webhookID, "webhookID"); len(errors) != 0 { + if errors := h.validator.ValidateUUID(webhookID, "webhookID"); len(errors) != 0 { msg := fmt.Sprintf("validation errors [%s], while deleting webhook with ID [%s]", spew.Sdump(errors), webhookID) ctxLogger.Warn(stacktrace.NewError(msg)) return h.responseUnprocessableEntity(c, errors, "validation errors while deleting webhook") @@ -160,15 +159,15 @@ func (h *WebhookHandler) Store(c *fiber.Ctx) error { return h.responseUnprocessableEntity(c, errors, "validation errors while storing webhook") } - webhooks, err := h.service.Index(ctx, h.userIDFomContext(c), repositories.IndexParams{Skip: 0, Limit: 3}) + webhooks, err := h.service.Index(ctx, h.userIDFomContext(c), repositories.IndexParams{Skip: 0, Limit: 10}) if err != nil { ctxLogger.Error(stacktrace.Propagate(err, fmt.Sprintf("cannot index webhooks for user [%s]", h.userIDFomContext(c)))) - return h.responsePaymentRequired(c, "You can't create more than 1 webhook contact us to upgrade your account.") + return h.responseInternalServerError(c) } - if len(webhooks) > 1 { - ctxLogger.Warn(stacktrace.NewError(fmt.Sprintf("user with ID [%s] wants to create more than 2 webhooks", h.userIDFomContext(c)))) - return h.responsePaymentRequired(c, "You can't create more than 2 webhooks contact us to upgrade your account.") + if len(webhooks) == 10 { + ctxLogger.Warn(stacktrace.NewError(fmt.Sprintf("user with ID [%s] wants to create more than 5 webhooks", h.userIDFomContext(c)))) + return h.responsePaymentRequired(c, "You can't create more than 10 webhooks contact us to upgrade to our enterprise plan.") } webhook, err := h.service.Store(ctx, request.ToStoreParams(h.userFromContext(c))) diff --git a/api/pkg/listeners/billing_listener.go b/api/pkg/listeners/billing_listener.go index 67871a15..7e2cce00 100644 --- a/api/pkg/listeners/billing_listener.go +++ b/api/pkg/listeners/billing_listener.go @@ -34,6 +34,7 @@ func NewBillingListener( return l, map[string]events.EventListener{ events.EventTypeMessageAPISent: l.OnMessageAPISent, + events.UserAccountDeleted: l.onUserAccountDeleted, events.EventTypeMessagePhoneReceived: l.OnMessagePhoneReceived, } } @@ -75,3 +76,21 @@ func (listener *BillingListener) OnMessagePhoneReceived(ctx context.Context, eve return nil } + +func (listener *BillingListener) onUserAccountDeleted(ctx context.Context, event cloudevents.Event) error { + ctx, span := listener.tracer.Start(ctx) + defer span.End() + + var payload events.UserAccountDeletedPayload + if err := event.DataAs(&payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := listener.service.DeleteAllForUser(ctx, payload.UserID); err != nil { + msg := fmt.Sprintf("cannot delete [entities.BillingUsage] for user [%s] on [%s] event with ID [%s]", payload.UserID, event.Type(), event.ID()) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} diff --git a/api/pkg/listeners/discord_listener.go b/api/pkg/listeners/discord_listener.go index 89d30b03..b590edcd 100644 --- a/api/pkg/listeners/discord_listener.go +++ b/api/pkg/listeners/discord_listener.go @@ -32,6 +32,7 @@ func NewDiscordListener( return l, map[string]events.EventListener{ events.EventTypeMessagePhoneReceived: l.OnMessagePhoneReceived, + events.UserAccountDeleted: l.onUserAccountDeleted, } } @@ -53,3 +54,21 @@ func (listener *DiscordListener) OnMessagePhoneReceived(ctx context.Context, eve return nil } + +func (listener *DiscordListener) onUserAccountDeleted(ctx context.Context, event cloudevents.Event) error { + ctx, span := listener.tracer.Start(ctx) + defer span.End() + + var payload events.UserAccountDeletedPayload + if err := event.DataAs(&payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := listener.service.DeleteAllForUser(ctx, payload.UserID); err != nil { + msg := fmt.Sprintf("cannot delete [entities.Discord] for user [%s] on [%s] event with ID [%s]", payload.UserID, event.Type(), event.ID()) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} diff --git a/api/pkg/listeners/heartbeat_listener.go b/api/pkg/listeners/heartbeat_listener.go index b8a631a1..573a029b 100644 --- a/api/pkg/listeners/heartbeat_listener.go +++ b/api/pkg/listeners/heartbeat_listener.go @@ -33,9 +33,11 @@ func NewHeartbeatListener( } return l, map[string]events.EventListener{ - events.EventTypePhoneUpdated: l.onPhoneUpdated, - events.EventTypePhoneDeleted: l.onPhoneDeleted, - events.EventTypePhoneHeartbeatCheck: l.onPhoneHeartbeatCheck, + events.EventTypePhoneUpdated: l.onPhoneUpdated, + events.EventTypePhoneDeleted: l.onPhoneDeleted, + events.EventTypePhoneHeartbeatCheck: l.onPhoneHeartbeatCheck, + events.EventTypePhoneHeartbeatOffline: l.onPhoneHeartbeatOffline, + events.UserAccountDeleted: l.onUserAccountDeleted, } } @@ -110,3 +112,40 @@ func (listener *HeartbeatListener) onPhoneHeartbeatCheck(ctx context.Context, ev return nil } + +// onPhoneDeleted handles the events.EventTypePhoneDeleted event +func (listener *HeartbeatListener) onPhoneHeartbeatOffline(ctx context.Context, event cloudevents.Event) error { + ctx, span := listener.tracer.Start(ctx) + defer span.End() + + var payload events.PhoneHeartbeatOfflinePayload + if err := event.DataAs(&payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := listener.service.UpdatePhoneOnline(ctx, payload.UserID, payload.MonitorID, false); err != nil { + msg := fmt.Sprintf("cannot delete heartbeat monitor with userID [%s] and owner [%s] for event with ID [%s]", payload.UserID, payload.Owner, event.ID()) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + +func (listener *HeartbeatListener) onUserAccountDeleted(ctx context.Context, event cloudevents.Event) error { + ctx, span := listener.tracer.Start(ctx) + defer span.End() + + var payload events.UserAccountDeletedPayload + if err := event.DataAs(&payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := listener.service.DeleteAllForUser(ctx, payload.UserID); err != nil { + msg := fmt.Sprintf("cannot delete [entities.Heartbeat] for user [%s] on [%s] event with ID [%s]", payload.UserID, event.Type(), event.ID()) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} diff --git a/api/pkg/listeners/integration_3cx_listener.go b/api/pkg/listeners/integration_3cx_listener.go index b4b24cda..07d0c803 100644 --- a/api/pkg/listeners/integration_3cx_listener.go +++ b/api/pkg/listeners/integration_3cx_listener.go @@ -31,10 +31,11 @@ func NewIntegration3CXListener( } return l, map[string]events.EventListener{ - events.EventTypeMessagePhoneReceived: l.OnMessagePhoneReceived, - events.EventTypeMessagePhoneDelivered: l.OnMessagePhoneDelivered, - events.EventTypeMessageSendFailed: l.OnMessageSendFailed, - events.EventTypeMessagePhoneSent: l.OnMessagePhoneSent, + // events.EventTypeMessagePhoneReceived: l.OnMessagePhoneReceived, + // events.EventTypeMessagePhoneDelivered: l.OnMessagePhoneDelivered, + // events.EventTypeMessageSendFailed: l.OnMessageSendFailed, + // events.EventTypeMessagePhoneSent: l.OnMessagePhoneSent, + events.UserAccountDeleted: l.onUserAccountDeleted, } } @@ -113,3 +114,21 @@ func (listener *Integration3CXListener) OnMessagePhoneDelivered(ctx context.Cont return nil } + +func (listener *Integration3CXListener) onUserAccountDeleted(ctx context.Context, event cloudevents.Event) error { + ctx, span := listener.tracer.Start(ctx) + defer span.End() + + var payload events.UserAccountDeletedPayload + if err := event.DataAs(&payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := listener.service.DeleteAllForUser(ctx, payload.UserID); err != nil { + msg := fmt.Sprintf("cannot delete [entities.Integration3CX] for user [%s] on [%s] event with ID [%s]", payload.UserID, event.Type(), event.ID()) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} diff --git a/api/pkg/listeners/marketing_listener.go b/api/pkg/listeners/marketing_listener.go new file mode 100644 index 00000000..62da4829 --- /dev/null +++ b/api/pkg/listeners/marketing_listener.go @@ -0,0 +1,73 @@ +package listeners + +import ( + "context" + "fmt" + + "github.com/NdoleStudio/httpsms/pkg/events" + "github.com/NdoleStudio/httpsms/pkg/services" + "github.com/NdoleStudio/httpsms/pkg/telemetry" + cloudevents "github.com/cloudevents/sdk-go/v2" + "github.com/palantir/stacktrace" +) + +// MarketingListener handled marketing events +type MarketingListener struct { + logger telemetry.Logger + tracer telemetry.Tracer + service *services.MarketingService +} + +// NewMarketingListener creates a new instance of MarketingListener +func NewMarketingListener( + logger telemetry.Logger, + tracer telemetry.Tracer, + service *services.MarketingService, +) (l *MarketingListener, routes map[string]events.EventListener) { + l = &MarketingListener{ + logger: logger.WithService(fmt.Sprintf("%T", l)), + tracer: tracer, + service: service, + } + + return l, map[string]events.EventListener{ + events.UserAccountDeleted: l.onUserAccountDeleted, + events.UserAccountCreated: l.onUserAccountCreated, + } +} + +func (listener *MarketingListener) onUserAccountCreated(ctx context.Context, event cloudevents.Event) error { + ctx, span := listener.tracer.Start(ctx) + defer span.End() + + var payload events.UserAccountCreatedPayload + if err := event.DataAs(&payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := listener.service.CreateContact(ctx, payload.UserID); err != nil { + msg := fmt.Sprintf("cannot create [contact] for user [%s] on [%s] event with ID [%s]", payload.UserID, event.Type(), event.ID()) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + +func (listener *MarketingListener) onUserAccountDeleted(ctx context.Context, event cloudevents.Event) error { + ctx, span := listener.tracer.Start(ctx) + defer span.End() + + var payload events.UserAccountDeletedPayload + if err := event.DataAs(&payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := listener.service.DeleteContact(ctx, payload.UserEmail); err != nil { + msg := fmt.Sprintf("cannot delete [contact] for user [%s] on [%s] event with ID [%s]", payload.UserID, event.Type(), event.ID()) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} diff --git a/api/pkg/listeners/message_listener.go b/api/pkg/listeners/message_listener.go index 96ef2056..770fe079 100644 --- a/api/pkg/listeners/message_listener.go +++ b/api/pkg/listeners/message_listener.go @@ -41,6 +41,9 @@ func NewMessageListener( events.EventTypeMessageSendExpiredCheck: l.onMessageSendExpiredCheck, events.EventTypeMessageSendExpired: l.onMessageSendExpired, events.EventTypeMessageNotificationScheduled: l.onMessageNotificationScheduled, + events.MessageThreadAPIDeleted: l.onMessageThreadAPIDeleted, + events.MessageCallMissed: l.onMessageCallMissed, + events.UserAccountDeleted: l.onUserAccountDeleted, } } @@ -233,7 +236,7 @@ func (listener *MessageListener) onMessageSendExpiredCheck(ctx context.Context, Source: event.Source(), } if err := listener.service.CheckExpired(ctx, checkParams); err != nil { - msg := fmt.Sprintf("cannot check expiration for ID [%s] and userID [%s]", checkParams.MessageID, checkParams.UserID) + msg := fmt.Sprintf("cannot check expiration for message with ID [%s] and userID [%s]", checkParams.MessageID, checkParams.UserID) return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) } @@ -289,3 +292,59 @@ func (listener *MessageListener) onMessageNotificationScheduled(ctx context.Cont return nil } + +// onMessageThreadAPIDeleted handles the events.MessageThreadAPIDeleted event +func (listener *MessageListener) onMessageThreadAPIDeleted(ctx context.Context, event cloudevents.Event) error { + ctx, span := listener.tracer.Start(ctx) + defer span.End() + + var payload events.MessageThreadAPIDeletedPayload + if err := event.DataAs(&payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := listener.service.DeleteByOwnerAndContact(ctx, payload.UserID, payload.Owner, payload.Contact); err != nil { + msg := fmt.Sprintf("cannot handle [%s] event with ID [%s] and userID [%s]", event.Type(), event.ID(), payload.UserID) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + +// onMessageThreadAPIDeleted handles the events.MessageThreadAPIDeleted event +func (listener *MessageListener) onMessageCallMissed(ctx context.Context, event cloudevents.Event) error { + ctx, span := listener.tracer.Start(ctx) + defer span.End() + + payload := new(events.MessageCallMissedPayload) + if err := event.DataAs(payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := listener.service.RespondToMissedCall(ctx, event.Source(), payload); err != nil { + msg := fmt.Sprintf("cannot handle [%s] event with ID [%s] and userID [%s]", event.Type(), event.ID(), payload.UserID) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + +func (listener *MessageListener) onUserAccountDeleted(ctx context.Context, event cloudevents.Event) error { + ctx, span := listener.tracer.Start(ctx) + defer span.End() + + var payload events.UserAccountDeletedPayload + if err := event.DataAs(&payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := listener.service.DeleteAllForUser(ctx, payload.UserID); err != nil { + msg := fmt.Sprintf("cannot delete [entities.Message] for user [%s] on [%s] event with ID [%s]", payload.UserID, event.Type(), event.ID()) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} diff --git a/api/pkg/listeners/message_thread_listener.go b/api/pkg/listeners/message_thread_listener.go index 9168dab5..e86c8092 100644 --- a/api/pkg/listeners/message_thread_listener.go +++ b/api/pkg/listeners/message_thread_listener.go @@ -34,6 +34,7 @@ func NewMessageThreadListener( return l, map[string]events.EventListener{ events.EventTypeMessageAPISent: l.OnMessageAPISent, + events.MessageAPIDeleted: l.onMessageDeleted, events.EventTypeMessagePhoneSending: l.OnMessagePhoneSending, events.EventTypeMessagePhoneSent: l.OnMessagePhoneSent, events.EventTypeMessagePhoneDelivered: l.OnMessagePhoneDelivered, @@ -41,6 +42,7 @@ func NewMessageThreadListener( events.EventTypeMessagePhoneReceived: l.OnMessagePhoneReceived, events.EventTypeMessageNotificationScheduled: l.onMessageNotificationScheduled, events.EventTypeMessageSendExpired: l.onMessageExpired, + events.UserAccountDeleted: l.onUserAccountDeleted, } } @@ -73,6 +75,25 @@ func (listener *MessageThreadListener) OnMessageAPISent(ctx context.Context, eve return nil } +// onMessageDeleted handles the events.MessageAPIDeleted event +func (listener *MessageThreadListener) onMessageDeleted(ctx context.Context, event cloudevents.Event) error { + ctx, span := listener.tracer.Start(ctx) + defer span.End() + + payload := new(events.MessageAPIDeletedPayload) + if err := event.DataAs(payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := listener.service.UpdateAfterDeletedMessage(ctx, payload); err != nil { + msg := fmt.Sprintf("cannot update thread for message with ID [%s] for event with ID [%s]", payload.MessageID, event.ID()) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + // OnMessagePhoneSending handles the events.EventTypeMessagePhoneSending event func (listener *MessageThreadListener) OnMessagePhoneSending(ctx context.Context, event cloudevents.Event) error { ctx, span := listener.tracer.Start(ctx) @@ -276,6 +297,24 @@ func (listener *MessageThreadListener) onMessageExpired(ctx context.Context, eve return nil } +func (listener *MessageThreadListener) onUserAccountDeleted(ctx context.Context, event cloudevents.Event) error { + ctx, span := listener.tracer.Start(ctx) + defer span.End() + + var payload events.UserAccountDeletedPayload + if err := event.DataAs(&payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := listener.service.DeleteAllForUser(ctx, payload.UserID); err != nil { + msg := fmt.Sprintf("cannot delete [entities.MessageThread] for user [%s] on [%s] event with ID [%s]", payload.UserID, event.Type(), event.ID()) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + func (listener *MessageThreadListener) updateThread(ctx context.Context, params services.MessageThreadUpdateParams) error { return listener.service.UpdateThread(ctx, params) } diff --git a/api/pkg/listeners/phone_api_key_listener.go b/api/pkg/listeners/phone_api_key_listener.go new file mode 100644 index 00000000..626063f0 --- /dev/null +++ b/api/pkg/listeners/phone_api_key_listener.go @@ -0,0 +1,104 @@ +package listeners + +import ( + "context" + "fmt" + + "github.com/NdoleStudio/httpsms/pkg/entities" + + cloudevents "github.com/cloudevents/sdk-go/v2" + "github.com/davecgh/go-spew/spew" + "github.com/palantir/stacktrace" + + "github.com/NdoleStudio/httpsms/pkg/events" + "github.com/NdoleStudio/httpsms/pkg/services" + "github.com/NdoleStudio/httpsms/pkg/telemetry" +) + +// PhoneAPIKeyListener handles cloud events that alter the state of entities.PhoneAPIKey +type PhoneAPIKeyListener struct { + logger telemetry.Logger + tracer telemetry.Tracer + service *services.PhoneAPIKeyService +} + +// NewPhoneAPIKeyListener creates a new instance of PhoneAPIKeyListener +func NewPhoneAPIKeyListener( + logger telemetry.Logger, + tracer telemetry.Tracer, + service *services.PhoneAPIKeyService, +) (l *PhoneAPIKeyListener, routes map[string]events.EventListener) { + l = &PhoneAPIKeyListener{ + logger: logger.WithService(fmt.Sprintf("%T", l)), + tracer: tracer, + service: service, + } + + return l, map[string]events.EventListener{ + events.EventTypePhoneUpdated: l.onPhoneUpdated, + events.EventTypePhoneDeleted: l.onPhoneDeleted, + events.UserAccountDeleted: l.onUserAccountDeleted, + } +} + +// onPhoneUpdated handles the events.EventTypePhoneUpdated event +func (listener *PhoneAPIKeyListener) onPhoneUpdated(ctx context.Context, event cloudevents.Event) error { + ctx, span, ctxLogger := listener.tracer.StartWithLogger(ctx, listener.logger) + defer span.End() + + var payload events.PhoneUpdatedPayload + if err := event.DataAs(&payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if payload.PhoneAPIKeyID == nil { + ctxLogger.Info(fmt.Sprintf("phone API Key does not exist for [%s] event with ID [%s] and phone with ID [%s] for user [%S]", event.Type(), event.ID(), payload.PhoneID, payload.UserID)) + return nil + } + + if err := listener.service.AddPhone(ctx, payload.UserID, *payload.PhoneAPIKeyID, payload.PhoneID); err != nil { + msg := fmt.Sprintf("cannot store heartbeat monitor with params [%s] for event with ID [%s]", spew.Sdump(payload), event.ID()) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + +// onPhoneUpdated handles the events.EventTypePhoneUpdated event +func (listener *PhoneAPIKeyListener) onPhoneDeleted(ctx context.Context, event cloudevents.Event) error { + ctx, span, _ := listener.tracer.StartWithLogger(ctx, listener.logger) + defer span.End() + + var payload events.PhoneDeletedPayload + if err := event.DataAs(&payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := listener.service.RemovePhoneByID(ctx, payload.UserID, payload.PhoneID, payload.Owner); err != nil { + msg := fmt.Sprintf("cannot remove phone with ID [%s] from phone api key for [%s] event with ID [%s]", payload.PhoneID, event.Type(), event.ID()) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + +// onUserAccountDeleted handles the events.EventTypePhoneUpdated event +func (listener *PhoneAPIKeyListener) onUserAccountDeleted(ctx context.Context, event cloudevents.Event) error { + ctx, span, _ := listener.tracer.StartWithLogger(ctx, listener.logger) + defer span.End() + + var payload events.UserAccountDeletedPayload + if err := event.DataAs(&payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := listener.service.DeleteAllForUser(ctx, payload.UserID); err != nil { + msg := fmt.Sprintf("cannot delete all [%T] for user with ID [%s] for [%s] event with ID [%s]", entities.PhoneAPIKey{}, payload.UserID, event.Type(), event.ID()) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} diff --git a/api/pkg/listeners/phone_notification_listener.go b/api/pkg/listeners/phone_notification_listener.go index f1d6d31f..e1b3eef7 100644 --- a/api/pkg/listeners/phone_notification_listener.go +++ b/api/pkg/listeners/phone_notification_listener.go @@ -37,6 +37,7 @@ func NewNotificationListener( events.EventTypeMessageSendRetry: l.onMessageSendRetry, events.EventTypeMessageNotificationSend: l.onMessageNotificationSend, events.PhoneHeartbeatMissed: l.onPhoneHeartbeatMissed, + events.UserAccountDeleted: l.onUserAccountDeleted, } } @@ -57,6 +58,7 @@ func (listener *PhoneNotificationListener) onMessageAPISent(ctx context.Context, Contact: payload.Contact, Content: payload.Content, SIM: payload.SIM, + Encrypted: payload.Encrypted, Source: event.Source(), MessageID: payload.MessageID, } @@ -86,6 +88,7 @@ func (listener *PhoneNotificationListener) onMessageSendRetry(ctx context.Contex Contact: payload.Contact, Content: payload.Content, SIM: payload.SIM, + Encrypted: payload.Encrypted, Source: event.Source(), MessageID: payload.MessageID, } @@ -144,3 +147,21 @@ func (listener *PhoneNotificationListener) onMessageNotificationSend(ctx context return nil } + +func (listener *PhoneNotificationListener) onUserAccountDeleted(ctx context.Context, event cloudevents.Event) error { + ctx, span := listener.tracer.Start(ctx) + defer span.End() + + var payload events.UserAccountDeletedPayload + if err := event.DataAs(&payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := listener.service.DeleteAllForUser(ctx, payload.UserID); err != nil { + msg := fmt.Sprintf("cannot delete [entities.Phone] for user [%s] on [%s] event with ID [%s]", payload.UserID, event.Type(), event.ID()) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} diff --git a/api/pkg/listeners/user_listener.go b/api/pkg/listeners/user_listener.go index 06b1de8d..d9518a45 100644 --- a/api/pkg/listeners/user_listener.go +++ b/api/pkg/listeners/user_listener.go @@ -33,19 +33,22 @@ func NewUserListener( } return l, map[string]events.EventListener{ - events.EventTypePhoneHeartbeatDead: l.onPhoneHeartbeatDead, - events.UserSubscriptionCreated: l.OnUserSubscriptionCreated, - events.UserSubscriptionCancelled: l.OnUserSubscriptionCancelled, - events.UserSubscriptionExpired: l.OnUserSubscriptionExpired, + events.EventTypePhoneHeartbeatOffline: l.onPhoneHeartbeatDead, + events.UserSubscriptionCreated: l.OnUserSubscriptionCreated, + events.UserSubscriptionCancelled: l.OnUserSubscriptionCancelled, + events.UserSubscriptionUpdated: l.OnUserSubscriptionUpdated, + events.UserSubscriptionExpired: l.OnUserSubscriptionExpired, + events.UserAPIKeyRotated: l.onUserAPIKeyRotated, + events.UserAccountDeleted: l.onUserAccountDeleted, } } -// onPhoneHeartbeatDead handles the events.EventTypePhoneHeartbeatDead event +// onPhoneHeartbeatDead handles the events.EventTypePhoneHeartbeatOffline event func (listener *UserListener) onPhoneHeartbeatDead(ctx context.Context, event cloudevents.Event) error { ctx, span := listener.tracer.Start(ctx) defer span.End() - var payload events.PhoneHeartbeatDeadPayload + var payload events.PhoneHeartbeatOfflinePayload if err := event.DataAs(&payload); err != nil { msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) @@ -66,6 +69,25 @@ func (listener *UserListener) onPhoneHeartbeatDead(ctx context.Context, event cl return nil } +// onAPIKeyRotated handles the events.UserAPIKeyRotated event +func (listener *UserListener) onUserAPIKeyRotated(ctx context.Context, event cloudevents.Event) error { + ctx, span := listener.tracer.Start(ctx) + defer span.End() + + payload := new(events.UserAPIKeyRotatedPayload) + if err := event.DataAs(&payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := listener.service.SendAPIKeyRotatedEmail(ctx, payload); err != nil { + msg := fmt.Sprintf("cannot send notification with params [%s] for event with ID [%s]", spew.Sdump(payload), event.ID()) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + // OnUserSubscriptionCreated handles the events.UserSubscriptionCreated event func (listener *UserListener) OnUserSubscriptionCreated(ctx context.Context, event cloudevents.Event) error { ctx, span := listener.tracer.Start(ctx) @@ -122,3 +144,40 @@ func (listener *UserListener) OnUserSubscriptionExpired(ctx context.Context, eve return nil } + +// OnUserSubscriptionUpdated handles the events.UserSubscriptionUpdated event +func (listener *UserListener) OnUserSubscriptionUpdated(ctx context.Context, event cloudevents.Event) error { + ctx, span := listener.tracer.Start(ctx) + defer span.End() + + var payload events.UserSubscriptionUpdatedPayload + if err := event.DataAs(&payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := listener.service.UpdateSubscription(ctx, &payload); err != nil { + msg := fmt.Sprintf("cannot expire subscription for user with ID [%s] for event with ID [%s]", payload.UserID, event.ID()) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + +func (listener *UserListener) onUserAccountDeleted(ctx context.Context, event cloudevents.Event) error { + ctx, span := listener.tracer.Start(ctx) + defer span.End() + + var payload events.UserAccountDeletedPayload + if err := event.DataAs(&payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := listener.service.DeleteAuthUser(ctx, payload.UserID); err != nil { + msg := fmt.Sprintf("cannot delete [entities.AuthUser] for user [%s] on [%s] event with ID [%s]", payload.UserID, event.Type(), event.ID()) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} diff --git a/api/pkg/listeners/webhook_listener.go b/api/pkg/listeners/webhook_listener.go index 92422d49..d0550ad0 100644 --- a/api/pkg/listeners/webhook_listener.go +++ b/api/pkg/listeners/webhook_listener.go @@ -36,6 +36,10 @@ func NewWebhookListener( events.EventTypeMessagePhoneDelivered: l.OnMessagePhoneDelivered, events.EventTypeMessageSendFailed: l.OnMessageSendFailed, events.EventTypeMessagePhoneSent: l.OnMessagePhoneSent, + events.EventTypePhoneHeartbeatOnline: l.onPhoneHeartbeatOnline, + events.EventTypePhoneHeartbeatOffline: l.onPhoneHeartbeatOffline, + events.MessageCallMissed: l.onMessageCallMissed, + events.UserAccountDeleted: l.onUserAccountDeleted, } } @@ -133,3 +137,78 @@ func (listener *WebhookListener) OnMessagePhoneDelivered(ctx context.Context, ev return nil } + +// OnMessagePhoneDelivered handles the events.EventTypeMessagePhoneDelivered event +func (listener *WebhookListener) onPhoneHeartbeatOffline(ctx context.Context, event cloudevents.Event) error { + ctx, span := listener.tracer.Start(ctx) + defer span.End() + + var payload events.PhoneHeartbeatOfflinePayload + if err := event.DataAs(&payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := listener.service.Send(ctx, payload.UserID, event, payload.Owner); err != nil { + msg := fmt.Sprintf("cannot process [%s] event with ID [%s]", event.Type(), event.ID()) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + +// OnMessagePhoneDelivered handles the events.EventTypeMessagePhoneDelivered event +func (listener *WebhookListener) onPhoneHeartbeatOnline(ctx context.Context, event cloudevents.Event) error { + ctx, span := listener.tracer.Start(ctx) + defer span.End() + + var payload events.PhoneHeartbeatOnlinePayload + if err := event.DataAs(&payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := listener.service.Send(ctx, payload.UserID, event, payload.Owner); err != nil { + msg := fmt.Sprintf("cannot process [%s] event with ID [%s]", event.Type(), event.ID()) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + +// onMessageCallMissed handles the events.MessageCallMissed event +func (listener *WebhookListener) onMessageCallMissed(ctx context.Context, event cloudevents.Event) error { + ctx, span := listener.tracer.Start(ctx) + defer span.End() + + var payload events.MessageCallMissedPayload + if err := event.DataAs(&payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := listener.service.Send(ctx, payload.UserID, event, payload.Owner); err != nil { + msg := fmt.Sprintf("cannot process [%s] event with ID [%s]", event.Type(), event.ID()) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + +func (listener *WebhookListener) onUserAccountDeleted(ctx context.Context, event cloudevents.Event) error { + ctx, span := listener.tracer.Start(ctx) + defer span.End() + + var payload events.UserAccountDeletedPayload + if err := event.DataAs(&payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := listener.service.DeleteAllForUser(ctx, payload.UserID); err != nil { + msg := fmt.Sprintf("cannot delete [entities.Webhook] for user [%s] on [%s] event with ID [%s]", payload.UserID, event.Type(), event.ID()) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} diff --git a/api/pkg/listeners/websocket_listener.go b/api/pkg/listeners/websocket_listener.go new file mode 100644 index 00000000..2c0e2c17 --- /dev/null +++ b/api/pkg/listeners/websocket_listener.go @@ -0,0 +1,116 @@ +package listeners + +import ( + "context" + "fmt" + + cloudevents "github.com/cloudevents/sdk-go/v2" + "github.com/palantir/stacktrace" + + "github.com/NdoleStudio/httpsms/pkg/events" + "github.com/NdoleStudio/httpsms/pkg/telemetry" + "github.com/pusher/pusher-http-go/v5" +) + +// WebsocketListener handles cloud events that send a websocket event to the frontend +type WebsocketListener struct { + logger telemetry.Logger + tracer telemetry.Tracer + client *pusher.Client +} + +// NewWebsocketListener creates a new instance of WebsocketListener +func NewWebsocketListener( + logger telemetry.Logger, + tracer telemetry.Tracer, + client *pusher.Client, +) (l *WebsocketListener, routes map[string]events.EventListener) { + l = &WebsocketListener{ + logger: logger.WithService(fmt.Sprintf("%T", l)), + tracer: tracer, + client: client, + } + + return l, map[string]events.EventListener{ + events.EventTypePhoneUpdated: l.onPhoneUpdated, + events.EventTypeMessagePhoneSent: l.onMessagePhoneSent, + events.EventTypeMessageSendFailed: l.onMessagePhoneFailed, + events.EventTypeMessagePhoneReceived: l.onMessagePhoneReceived, + } +} + +// onMessagePhoneSent handles the events.EventTypeMessagePhoneSent event +func (listener *WebsocketListener) onMessagePhoneSent(ctx context.Context, event cloudevents.Event) error { + ctx, span, _ := listener.tracer.StartWithLogger(ctx, listener.logger) + defer span.End() + + var payload events.MessagePhoneSentPayload + if err := event.DataAs(&payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := listener.client.Trigger(payload.UserID.String(), event.Type(), event.ID()); err != nil { + msg := fmt.Sprintf("cannot trigger websocket [%s] event with ID [%s] for user with ID [%s]", event.Type(), event.ID(), payload.UserID) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + +// onMessagePhoneReceived handles the events.EventTypeMessagePhoneReceived event +func (listener *WebsocketListener) onMessagePhoneReceived(ctx context.Context, event cloudevents.Event) error { + ctx, span, _ := listener.tracer.StartWithLogger(ctx, listener.logger) + defer span.End() + + var payload events.MessagePhoneReceivedPayload + if err := event.DataAs(&payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := listener.client.Trigger(payload.UserID.String(), event.Type(), event.ID()); err != nil { + msg := fmt.Sprintf("cannot trigger websocket [%s] event with ID [%s] for user with ID [%s]", event.Type(), event.ID(), payload.UserID) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + +// onMessagePhoneFailed handles the events.EventTypeMessageSendFailed event +func (listener *WebsocketListener) onMessagePhoneFailed(ctx context.Context, event cloudevents.Event) error { + ctx, span, _ := listener.tracer.StartWithLogger(ctx, listener.logger) + defer span.End() + + var payload events.MessageSendFailedPayload + if err := event.DataAs(&payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := listener.client.Trigger(payload.UserID.String(), event.Type(), event.ID()); err != nil { + msg := fmt.Sprintf("cannot trigger websocket [%s] event with ID [%s] for user with ID [%s]", event.Type(), event.ID(), payload.UserID) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + +// onPhoneUpdated handles the events.EventTypePhoneUpdated event +func (listener *WebsocketListener) onPhoneUpdated(ctx context.Context, event cloudevents.Event) error { + ctx, span, _ := listener.tracer.StartWithLogger(ctx, listener.logger) + defer span.End() + + var payload events.PhoneUpdatedPayload + if err := event.DataAs(&payload); err != nil { + msg := fmt.Sprintf("cannot decode [%s] into [%T]", event.Data(), payload) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := listener.client.Trigger(payload.UserID.String(), event.Type(), event.ID()); err != nil { + msg := fmt.Sprintf("cannot trigger websocket [%s] event with ID [%s] for user with ID [%s]", event.Type(), event.ID(), payload.UserID) + return listener.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} diff --git a/api/pkg/middlewares/api_key_auth_middleware.go b/api/pkg/middlewares/api_key_auth_middleware.go index e4066855..d971c94a 100644 --- a/api/pkg/middlewares/api_key_auth_middleware.go +++ b/api/pkg/middlewares/api_key_auth_middleware.go @@ -2,6 +2,7 @@ package middlewares import ( "fmt" + "strings" "github.com/NdoleStudio/httpsms/pkg/repositories" "github.com/NdoleStudio/httpsms/pkg/telemetry" @@ -19,19 +20,40 @@ func APIKeyAuth(logger telemetry.Logger, tracer telemetry.Tracer, userRepository ctxLogger := tracer.CtxLogger(logger, span) - apiKey := c.Get(authHeaderAPIKey) - if len(apiKey) == 0 || apiKey == "undefined" { - span.AddEvent(fmt.Sprintf("the request header has no [%s] header", authHeaderAPIKey)) + apiKey := getAPIKeyFromRequest(c) + if len(apiKey) == 0 || apiKey == "undefined" || strings.HasPrefix(apiKey, "pk_") { + span.AddEvent(fmt.Sprintf("the request header has no primary [%s] header", authHeaderAPIKey)) return c.Next() } - authUser, err := userRepository.LoadAuthUser(ctx, apiKey) + authUser, err := userRepository.LoadAuthContext(ctx, apiKey) if err != nil { ctxLogger.Error(stacktrace.Propagate(err, fmt.Sprintf("cannot load user with api key [%s]", apiKey))) return c.Next() } + c.Locals(ContextKeyAuthUserID, authUser) - ctxLogger.Info(fmt.Sprintf("[%T] set successfully for user with ID [%s]", authUser, authUser.ID)) return c.Next() } } + +func getAPIKeyFromRequest(c *fiber.Ctx) string { + apiKey := c.Get(authHeaderAPIKey) + if len(apiKey) != 0 { + return apiKey + } + + payload := struct { + APIKey string `json:"x-api-key" form:"x-api-key" query:"x-api-key"` + }{} + + if err := c.BodyParser(&payload); err == nil && payload.APIKey != "" { + return payload.APIKey + } + + if err := c.QueryParser(&payload); err != nil { + return "" + } + + return payload.APIKey +} diff --git a/api/pkg/middlewares/authenticated_middlesare.go b/api/pkg/middlewares/authenticated_middlesare.go index 8e9bac19..103d426c 100644 --- a/api/pkg/middlewares/authenticated_middlesare.go +++ b/api/pkg/middlewares/authenticated_middlesare.go @@ -23,7 +23,7 @@ func Authenticated(tracer telemetry.Tracer) fiber.Handler { _, span := tracer.StartFromFiberCtx(c, "middlewares.Authenticated") defer span.End() - if tokenUser, ok := c.Locals(ContextKeyAuthUserID).(entities.AuthUser); !ok || tokenUser.IsNoop() { + if tokenUser, ok := c.Locals(ContextKeyAuthUserID).(entities.AuthContext); !ok || tokenUser.IsNoop() { return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{ "status": "error", "message": "You are not authorized to carry out this request.", diff --git a/api/pkg/middlewares/bearer_api_key_auth_middleware.go b/api/pkg/middlewares/bearer_api_key_auth_middleware.go index e12face7..16d9ac5e 100644 --- a/api/pkg/middlewares/bearer_api_key_auth_middleware.go +++ b/api/pkg/middlewares/bearer_api_key_auth_middleware.go @@ -15,27 +15,22 @@ func BearerAPIKeyAuth(logger telemetry.Logger, tracer telemetry.Tracer, userRepo logger = logger.WithService("middlewares.APIKeyAuth") return func(c *fiber.Ctx) error { - ctx, span := tracer.StartFromFiberCtx(c, "middlewares.APIKeyAuth") + ctx, span, ctxLogger := tracer.StartFromFiberCtxWithLogger(c, logger, "middlewares.APIKeyAuth") defer span.End() - ctxLogger := tracer.CtxLogger(logger, span) - apiKey := strings.TrimSpace(strings.Replace(c.Get(authHeaderBearer), bearerScheme, "", 1)) if len(apiKey) == 0 { span.AddEvent(fmt.Sprintf("the request header has no [%s] api key", authHeaderAPIKey)) return c.Next() } - authUser, err := userRepository.LoadAuthUser(ctx, apiKey) + authUser, err := userRepository.LoadAuthContext(ctx, apiKey) if err != nil { ctxLogger.Error(stacktrace.Propagate(err, fmt.Sprintf("cannot load user with api key [%s] using header [%s]", apiKey, c.Get(authHeaderBearer)))) return c.Next() } c.Locals(ContextKeyAuthUserID, authUser) - - ctxLogger.Info(fmt.Sprintf("[%T] set successfully for user with ID [%s]", authUser, authUser.ID)) - return c.Next() } } diff --git a/api/pkg/middlewares/bearer_auth_middleware.go b/api/pkg/middlewares/bearer_auth_middleware.go index bd6563c1..ffd29f0d 100644 --- a/api/pkg/middlewares/bearer_auth_middleware.go +++ b/api/pkg/middlewares/bearer_auth_middleware.go @@ -40,14 +40,12 @@ func BearerAuth(logger telemetry.Logger, tracer telemetry.Tracer, authClient *au span.AddEvent(fmt.Sprintf("[%s] token is valid", bearerScheme)) - authUser := entities.AuthUser{ + authUser := entities.AuthContext{ Email: token.Claims["email"].(string), ID: entities.UserID(token.Claims["user_id"].(string)), } c.Locals(ContextKeyAuthUserID, authUser) - - ctxLogger.Info(fmt.Sprintf("[%T] set successfully for user with ID [%s]", authUser, authUser.ID)) return c.Next() } } diff --git a/api/pkg/middlewares/http_request_logger_middleware.go b/api/pkg/middlewares/http_request_logger_middleware.go index bc0146f0..75ddcae2 100644 --- a/api/pkg/middlewares/http_request_logger_middleware.go +++ b/api/pkg/middlewares/http_request_logger_middleware.go @@ -2,6 +2,7 @@ package middlewares import ( "fmt" + "slices" "github.com/NdoleStudio/httpsms/pkg/telemetry" "github.com/gofiber/fiber/v2" @@ -18,17 +19,12 @@ func HTTPRequestLogger(tracer telemetry.Tracer, logger telemetry.Logger) fiber.H _, span, ctxLogger := tracer.StartFromFiberCtxWithLogger(c, logger) defer span.End() - ctxLogger.WithString("http.method", c.Method()). - WithString("http.path", c.Path()). - WithString("client.version", c.Get(clientVersionHeader)). - Trace(fmt.Sprintf("%s %s", c.Method(), c.OriginalURL())) - response := c.Next() statusCode := c.Response().StatusCode() span.AddEvent(fmt.Sprintf("finished handling request with traceID: [%s], statusCode: [%d]", span.SpanContext().TraceID().String(), statusCode)) - if statusCode >= 300 && len(c.Request().Body()) > 0 { - ctxLogger.Warn(stacktrace.NewError(fmt.Sprintf("http.status [%d], body [%s]", statusCode, string(c.Request().Body())))) + if statusCode >= 300 && len(c.Request().Body()) > 0 && !slices.Contains([]int{401, 402}, statusCode) { + ctxLogger.WithString("client.version", c.Get(clientVersionHeader)).Warn(stacktrace.NewError(fmt.Sprintf("http.status [%d], body [%s]", statusCode, string(c.Request().Body())))) } return response diff --git a/api/pkg/middlewares/phone_api_key_auth_middleware.go b/api/pkg/middlewares/phone_api_key_auth_middleware.go new file mode 100644 index 00000000..72bc75ae --- /dev/null +++ b/api/pkg/middlewares/phone_api_key_auth_middleware.go @@ -0,0 +1,36 @@ +package middlewares + +import ( + "fmt" + "strings" + + "github.com/NdoleStudio/httpsms/pkg/repositories" + "github.com/NdoleStudio/httpsms/pkg/telemetry" + "github.com/gofiber/fiber/v2" + "github.com/palantir/stacktrace" +) + +// PhoneAPIKeyAuth authenticates a user from the X-API-Key header +func PhoneAPIKeyAuth(logger telemetry.Logger, tracer telemetry.Tracer, repository repositories.PhoneAPIKeyRepository) fiber.Handler { + logger = logger.WithService("middlewares.APIKeyAuth") + + return func(c *fiber.Ctx) error { + ctx, span, ctxLogger := tracer.StartFromFiberCtxWithLogger(c, logger, "middlewares.APIKeyAuth") + defer span.End() + + apiKey := c.Get(authHeaderAPIKey) + if len(apiKey) == 0 || apiKey == "undefined" || !strings.HasPrefix(apiKey, "pk_") { + span.AddEvent(fmt.Sprintf("the request header has no [%s] header for the phone key", authHeaderAPIKey)) + return c.Next() + } + + authUser, err := repository.LoadAuthContext(ctx, apiKey) + if err != nil { + ctxLogger.Error(stacktrace.Propagate(err, fmt.Sprintf("cannot load user with phone api key [%s]", apiKey))) + return c.Next() + } + + c.Locals(ContextKeyAuthUserID, authUser) + return c.Next() + } +} diff --git a/api/pkg/repositories/attachment_repository.go b/api/pkg/repositories/attachment_repository.go new file mode 100644 index 00000000..11d80e20 --- /dev/null +++ b/api/pkg/repositories/attachment_repository.go @@ -0,0 +1,99 @@ +package repositories + +import ( + "context" + "fmt" + "path/filepath" + "strings" +) + +// AttachmentRepository is the interface for storing and retrieving message attachments +type AttachmentRepository interface { + // Upload stores attachment data at the given path with the specified content type + Upload(ctx context.Context, path string, data []byte, contentType string) error + // Download retrieves attachment data from the given path + Download(ctx context.Context, path string) ([]byte, error) + // Delete removes an attachment at the given path + Delete(ctx context.Context, path string) error +} + +// contentTypeExtensions maps MIME types to file extensions +var contentTypeExtensions = map[string]string{ + "image/jpeg": ".jpg", + "image/png": ".png", + "image/gif": ".gif", + "image/webp": ".webp", + "image/bmp": ".bmp", + "video/mp4": ".mp4", + "video/3gpp": ".3gp", + "audio/mpeg": ".mp3", + "audio/ogg": ".ogg", + "audio/amr": ".amr", + "application/pdf": ".pdf", + "text/vcard": ".vcf", + "text/x-vcard": ".vcf", +} + +// extensionContentTypes is the reverse map from file extensions to canonical MIME types +var extensionContentTypes = map[string]string{ + ".jpg": "image/jpeg", + ".png": "image/png", + ".gif": "image/gif", + ".webp": "image/webp", + ".bmp": "image/bmp", + ".mp4": "video/mp4", + ".3gp": "video/3gpp", + ".mp3": "audio/mpeg", + ".ogg": "audio/ogg", + ".amr": "audio/amr", + ".pdf": "application/pdf", + ".vcf": "text/vcard", +} + +// AllowedContentTypes returns the set of allowed MIME types for attachments +func AllowedContentTypes() map[string]bool { + allowed := make(map[string]bool, len(contentTypeExtensions)) + for ct := range contentTypeExtensions { + allowed[ct] = true + } + return allowed +} + +// ExtensionFromContentType returns the file extension for a MIME content type. +// Returns ".bin" if the content type is not recognized. +func ExtensionFromContentType(contentType string) string { + if ext, ok := contentTypeExtensions[contentType]; ok { + return ext + } + return ".bin" +} + +// ContentTypeFromExtension returns the MIME content type for a file extension. +// Returns "application/octet-stream" if the extension is not recognized. +func ContentTypeFromExtension(ext string) string { + if ct, ok := extensionContentTypes[ext]; ok { + return ct + } + return "application/octet-stream" +} + +// SanitizeFilename removes path separators and traversal sequences from a filename. +// Returns "attachment-{index}" if the sanitized name is empty. +func SanitizeFilename(name string, index int) string { + name = strings.TrimSuffix(name, filepath.Ext(name)) + + var builder strings.Builder + for _, r := range name { + if (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') || r == '_' || r == '-' { + builder.WriteRune(r) + } else if r == ' ' { + builder.WriteRune('-') + } + } + name = strings.Trim(builder.String(), "-") + + if name == "" { + return fmt.Sprintf("attachment-%d", index) + } + return name +} diff --git a/api/pkg/repositories/attachment_repository_test.go b/api/pkg/repositories/attachment_repository_test.go new file mode 100644 index 00000000..1b29fa68 --- /dev/null +++ b/api/pkg/repositories/attachment_repository_test.go @@ -0,0 +1,63 @@ +package repositories + +import "testing" + +func TestExtensionFromContentType(t *testing.T) { + tests := []struct { + contentType string + expected string + }{ + {"image/jpeg", ".jpg"}, + {"image/png", ".png"}, + {"image/gif", ".gif"}, + {"image/webp", ".webp"}, + {"image/bmp", ".bmp"}, + {"video/mp4", ".mp4"}, + {"video/3gpp", ".3gp"}, + {"audio/mpeg", ".mp3"}, + {"audio/ogg", ".ogg"}, + {"audio/amr", ".amr"}, + {"application/pdf", ".pdf"}, + {"text/vcard", ".vcf"}, + {"text/x-vcard", ".vcf"}, + {"application/octet-stream", ".bin"}, + {"unknown/type", ".bin"}, + {"", ".bin"}, + } + for _, tt := range tests { + t.Run(tt.contentType, func(t *testing.T) { + got := ExtensionFromContentType(tt.contentType) + if got != tt.expected { + t.Errorf("ExtensionFromContentType(%q) = %q, want %q", tt.contentType, got, tt.expected) + } + }) + } +} + +func TestSanitizeFilename(t *testing.T) { + tests := []struct { + name string + index int + expected string + }{ + {"photo.jpg", 0, "photo"}, + {"../../etc/passwd", 0, "etcpasswd"}, + {"hello/world\\test", 0, "helloworldtest"}, + {"normal_file", 0, "normal_file"}, + {"", 0, "attachment-0"}, + {" ", 0, "attachment-0"}, + {"...", 1, "attachment-1"}, + {"My Photo", 0, "My-Photo"}, + {"file name with spaces.png", 0, "file-name-with-spaces"}, + {"UPPER_CASE", 0, "UPPER_CASE"}, + {"special!@#chars", 0, "specialchars"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := SanitizeFilename(tt.name, tt.index) + if got != tt.expected { + t.Errorf("SanitizeFilename(%q, %d) = %q, want %q", tt.name, tt.index, got, tt.expected) + } + }) + } +} diff --git a/api/pkg/repositories/billing_usage_repository.go b/api/pkg/repositories/billing_usage_repository.go index 961d60d0..e9c4ffdb 100644 --- a/api/pkg/repositories/billing_usage_repository.go +++ b/api/pkg/repositories/billing_usage_repository.go @@ -20,4 +20,7 @@ type BillingUsageRepository interface { // GetHistory returns past billing usage by entities.UserID GetHistory(ctx context.Context, userID entities.UserID, params IndexParams) (*[]entities.BillingUsage, error) + + // DeleteForUser deletes all billing usage for an entities.UserID + DeleteAllForUser(ctx context.Context, userID entities.UserID) error } diff --git a/api/pkg/repositories/discord_repository.go b/api/pkg/repositories/discord_repository.go index 560ffcfd..13eba9c3 100644 --- a/api/pkg/repositories/discord_repository.go +++ b/api/pkg/repositories/discord_repository.go @@ -27,4 +27,7 @@ type DiscordRepository interface { // Delete an entities.Discord Delete(ctx context.Context, userID entities.UserID, DiscordID uuid.UUID) error + + // DeleteAllForUser deletes all entities.Discord for a user + DeleteAllForUser(ctx context.Context, userID entities.UserID) error } diff --git a/api/pkg/repositories/event_listener_log_repository.go b/api/pkg/repositories/event_listener_log_repository.go deleted file mode 100644 index a496c617..00000000 --- a/api/pkg/repositories/event_listener_log_repository.go +++ /dev/null @@ -1,16 +0,0 @@ -package repositories - -import ( - "context" - - "github.com/NdoleStudio/httpsms/pkg/entities" -) - -// EventListenerLogRepository loads and persists an entities.EventListenerLog -type EventListenerLogRepository interface { - // Store a new entities.EventListenerLog - Store(ctx context.Context, log *entities.EventListenerLog) error - - // Has verifies that the listener has not already been called - Has(ctx context.Context, eventID string, handler string) (bool, error) -} diff --git a/api/pkg/repositories/event_repository.go b/api/pkg/repositories/event_repository.go deleted file mode 100644 index a1343256..00000000 --- a/api/pkg/repositories/event_repository.go +++ /dev/null @@ -1,19 +0,0 @@ -package repositories - -import ( - "context" - - cloudevents "github.com/cloudevents/sdk-go/v2" -) - -// EventRepository is responsible for persisting cloudevents.Event -type EventRepository interface { - // Create a new entities.Message - Create(ctx context.Context, event cloudevents.Event) error - - // Save a new entities.Message - Save(ctx context.Context, event cloudevents.Event) error - - // FetchAll returns all cloudevents.Event ordered by time in ascending order - FetchAll(ctx context.Context) (*[]cloudevents.Event, error) -} diff --git a/api/pkg/repositories/google_cloud_storage_attachment_repository.go b/api/pkg/repositories/google_cloud_storage_attachment_repository.go new file mode 100644 index 00000000..d1e0eb92 --- /dev/null +++ b/api/pkg/repositories/google_cloud_storage_attachment_repository.go @@ -0,0 +1,92 @@ +package repositories + +import ( + "context" + "errors" + "fmt" + "io" + + "cloud.google.com/go/storage" + "github.com/NdoleStudio/httpsms/pkg/telemetry" + "github.com/palantir/stacktrace" +) + +// GoogleCloudStorageAttachmentRepository stores attachments in Google Cloud Storage +type GoogleCloudStorageAttachmentRepository struct { + logger telemetry.Logger + tracer telemetry.Tracer + client *storage.Client + bucket string +} + +// NewGoogleCloudStorageAttachmentRepository creates a new GoogleCloudStorageAttachmentRepository +func NewGoogleCloudStorageAttachmentRepository( + logger telemetry.Logger, + tracer telemetry.Tracer, + client *storage.Client, + bucket string, +) *GoogleCloudStorageAttachmentRepository { + return &GoogleCloudStorageAttachmentRepository{ + logger: logger.WithService(fmt.Sprintf("%T", &GoogleCloudStorageAttachmentRepository{})), + tracer: tracer, + client: client, + bucket: bucket, + } +} + +// Upload stores attachment data at the given path in GCS +func (s *GoogleCloudStorageAttachmentRepository) Upload(ctx context.Context, path string, data []byte, contentType string) error { + ctx, span, ctxLogger := s.tracer.StartWithLogger(ctx, s.logger) + defer span.End() + + writer := s.client.Bucket(s.bucket).Object(path).NewWriter(ctx) + writer.ContentType = contentType + + if _, err := writer.Write(data); err != nil { + return s.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, fmt.Sprintf("cannot write attachment to GCS path [%s]", path))) + } + + if err := writer.Close(); err != nil { + return s.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, fmt.Sprintf("cannot close GCS writer for path [%s]", path))) + } + + ctxLogger.Info(fmt.Sprintf("uploaded attachment to GCS path [%s/%s] with size [%d]", s.bucket, path, len(data))) + return nil +} + +// Download retrieves attachment data from the given path in GCS +func (s *GoogleCloudStorageAttachmentRepository) Download(ctx context.Context, path string) ([]byte, error) { + ctx, span, ctxLogger := s.tracer.StartWithLogger(ctx, s.logger) + defer span.End() + + reader, err := s.client.Bucket(s.bucket).Object(path).NewReader(ctx) + if err != nil { + msg := fmt.Sprintf("cannot open GCS reader for path [%s]", path) + if errors.Is(err, storage.ErrObjectNotExist) { + return nil, s.tracer.WrapErrorSpan(span, stacktrace.PropagateWithCode(err, ErrCodeNotFound, msg)) + } + return nil, s.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + defer reader.Close() + + data, err := io.ReadAll(reader) + if err != nil { + return nil, s.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, fmt.Sprintf("cannot read attachment from GCS path [%s]", path))) + } + + ctxLogger.Info(fmt.Sprintf("downloaded attachment from GCS path [%s/%s] with size [%d]", s.bucket, path, len(data))) + return data, nil +} + +// Delete removes an attachment at the given path in GCS +func (s *GoogleCloudStorageAttachmentRepository) Delete(ctx context.Context, path string) error { + ctx, span, ctxLogger := s.tracer.StartWithLogger(ctx, s.logger) + defer span.End() + + if err := s.client.Bucket(s.bucket).Object(path).Delete(ctx); err != nil { + return s.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, fmt.Sprintf("cannot delete GCS object at path [%s]", path))) + } + + ctxLogger.Info(fmt.Sprintf("deleted attachment from GCS path [%s/%s]", s.bucket, path)) + return nil +} diff --git a/api/pkg/repositories/gorm_billing_usage_repository.go b/api/pkg/repositories/gorm_billing_usage_repository.go index 62211618..4df8e65d 100644 --- a/api/pkg/repositories/gorm_billing_usage_repository.go +++ b/api/pkg/repositories/gorm_billing_usage_repository.go @@ -35,6 +35,19 @@ func NewGormBillingUsageRepository( } } +// DeleteForUser deletes all billing usages for a user +func (repository *gormBillingUsageRepository) DeleteAllForUser(ctx context.Context, userID entities.UserID) error { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + if err := repository.db.WithContext(ctx).Where("user_id = ?", userID).Delete(&entities.BillingUsage{}).Error; err != nil { + msg := fmt.Sprintf("cannot delete all [%T] for user with ID [%s]", &entities.BillingUsage{}, userID) + return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + // RegisterSentMessage registers a message as sent func (repository *gormBillingUsageRepository) RegisterSentMessage(ctx context.Context, timestamp time.Time, userID entities.UserID) error { ctx, span := repository.tracer.Start(ctx) diff --git a/api/pkg/repositories/gorm_discord_repository.go b/api/pkg/repositories/gorm_discord_repository.go index 9b08bbb3..796f6dfb 100644 --- a/api/pkg/repositories/gorm_discord_repository.go +++ b/api/pkg/repositories/gorm_discord_repository.go @@ -32,6 +32,18 @@ func NewGormDiscordRepository( } } +func (repository *gormDiscordRepository) DeleteAllForUser(ctx context.Context, userID entities.UserID) error { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + if err := repository.db.WithContext(ctx).Where("user_id = ?", userID).Delete(&entities.Discord{}).Error; err != nil { + msg := fmt.Sprintf("cannot delete all [%T] for user with ID [%s]", &entities.Discord{}, userID) + return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + func (repository *gormDiscordRepository) Save(ctx context.Context, Discord *entities.Discord) error { ctx, span := repository.tracer.Start(ctx) defer span.End() diff --git a/api/pkg/repositories/gorm_event_listener_log_repository.go b/api/pkg/repositories/gorm_event_listener_log_repository.go deleted file mode 100644 index 5b909620..00000000 --- a/api/pkg/repositories/gorm_event_listener_log_repository.go +++ /dev/null @@ -1,64 +0,0 @@ -package repositories - -import ( - "context" - "fmt" - - "github.com/NdoleStudio/httpsms/pkg/entities" - "github.com/NdoleStudio/httpsms/pkg/telemetry" - "github.com/palantir/stacktrace" - "gorm.io/gorm" -) - -// gormEventListenerLogRepository is responsible for persisting entities.EventListenerLog -type gormEventListenerLogRepository struct { - logger telemetry.Logger - tracer telemetry.Tracer - db *gorm.DB -} - -// NewGormEventListenerLogRepository creates the GORM version of the EventListenerLogRepository -func NewGormEventListenerLogRepository( - logger telemetry.Logger, - tracer telemetry.Tracer, - db *gorm.DB, -) EventListenerLogRepository { - return &gormEventListenerLogRepository{ - logger: logger.WithService(fmt.Sprintf("%T", &gormEventRepository{})), - tracer: tracer, - db: db, - } -} - -// Store a new entities.Message -func (repository *gormEventListenerLogRepository) Store(ctx context.Context, message *entities.EventListenerLog) error { - ctx, span := repository.tracer.Start(ctx) - defer span.End() - - if err := repository.db.WithContext(ctx).Create(message).Error; err != nil { - msg := fmt.Sprintf("cannot save message with ID [%s]", message.ID) - return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) - } - - return nil -} - -// Has checks if an event has been handled -func (repository *gormEventListenerLogRepository) Has(ctx context.Context, eventID string, handler string) (bool, error) { - ctx, span := repository.tracer.Start(ctx) - defer span.End() - - var exists bool - err := repository.db.WithContext(ctx).Model(&entities.EventListenerLog{}). - Select("count(*) > 0"). - Where("event_id = ?", eventID). - Where("handler = ?", handler). - Find(&exists). - Error - if err != nil { - msg := fmt.Sprintf("cannot check if log exists with event ID [%s] and handler [%s]", eventID, handler) - return exists, repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) - } - - return exists, nil -} diff --git a/api/pkg/repositories/gorm_event_repository.go b/api/pkg/repositories/gorm_event_repository.go deleted file mode 100644 index 0123891e..00000000 --- a/api/pkg/repositories/gorm_event_repository.go +++ /dev/null @@ -1,124 +0,0 @@ -package repositories - -import ( - "context" - "encoding/json" - "fmt" - "time" - - "github.com/NdoleStudio/httpsms/pkg/telemetry" - cloudevents "github.com/cloudevents/sdk-go/v2" - "github.com/google/uuid" - "github.com/palantir/stacktrace" - "gorm.io/datatypes" - "gorm.io/gorm" -) - -// GormEvent is a serialized version of cloudevents.Event -type GormEvent struct { - ID uuid.UUID `json:"id" gorm:"primaryKey;type:uuid;"` - Time time.Time - CreatedAt time.Time - Source string - Type string - Data datatypes.JSON -} - -// TableName overrides the table name used by GormEvent to `events` -func (GormEvent) TableName() string { - return "events" -} - -type gormEventRepository struct { - logger telemetry.Logger - tracer telemetry.Tracer - db *gorm.DB -} - -// NewGormEventRepository creates the GORM version of the EventRepository -func NewGormEventRepository( - logger telemetry.Logger, - tracer telemetry.Tracer, - db *gorm.DB, -) EventRepository { - return &gormEventRepository{ - logger: logger.WithService(fmt.Sprintf("%T", &gormEventRepository{})), - tracer: tracer, - db: db, - } -} - -// FetchAll returns all cloudevents.Event ordered by time in ascending order -func (repository *gormEventRepository) FetchAll(ctx context.Context) (*[]cloudevents.Event, error) { - ctx, span := repository.tracer.Start(ctx) - defer span.End() - - var events []GormEvent - if err := repository.db.WithContext(ctx).Order("time ASC").Find(&events).Error; err != nil { - msg := fmt.Sprintf("cannot fetch all cloudevents") - return nil, repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) - } - - results := make([]cloudevents.Event, 0, len(events)) - for _, event := range events { - var cloudevent cloudevents.Event - if err := json.Unmarshal(event.Data, &cloudevent); err != nil { - msg := fmt.Sprintf("cannot unmarshal [%s] into [%T]", event.Data, cloudevent) - return nil, repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) - } - results = append(results, cloudevent) - } - return &results, nil -} - -// Create creates a new cloudevents.Event -func (repository *gormEventRepository) Create(ctx context.Context, event cloudevents.Event) error { - ctx, span := repository.tracer.Start(ctx) - defer span.End() - - data, err := event.MarshalJSON() - if err != nil { - return stacktrace.Propagate(err, fmt.Sprintf("cannot marshall event [%s] and type [%s] into JSON", event.ID(), event.Type())) - } - - gormEvent := GormEvent{ - ID: uuid.MustParse(event.ID()), - Time: event.Time(), - Source: event.Source(), - CreatedAt: event.Time().UTC(), - Type: event.Type(), - Data: datatypes.JSON(data), - } - - if err = repository.db.WithContext(ctx).Create(gormEvent).Error; err != nil { - return stacktrace.Propagate(err, fmt.Sprintf("cannot create event [%s] and type [%s]", event.ID(), event.Type())) - } - - return nil -} - -// Save updates a cloudevents.Event -func (repository *gormEventRepository) Save(ctx context.Context, event cloudevents.Event) error { - ctx, span := repository.tracer.Start(ctx) - defer span.End() - - data, err := event.MarshalJSON() - if err != nil { - return stacktrace.Propagate(err, fmt.Sprintf("cannot marshall event [%s] and type [%s] into JSON", event.ID(), event.Type())) - } - - gormEvent := GormEvent{ - ID: uuid.MustParse(event.ID()), - Time: event.Time(), - Source: event.Source(), - CreatedAt: event.Time().UTC(), - Type: event.Type(), - Data: datatypes.JSON(data), - } - - if err = repository.db.WithContext(ctx).Save(gormEvent).Error; err != nil { - return stacktrace.Propagate(err, fmt.Sprintf("cannot save event [%s] and type [%s]", event.ID(), event.Type())) - } - - return nil -} diff --git a/api/pkg/repositories/gorm_heartbeat_monitor_repository.go b/api/pkg/repositories/gorm_heartbeat_monitor_repository.go index ae752c17..fb892d87 100644 --- a/api/pkg/repositories/gorm_heartbeat_monitor_repository.go +++ b/api/pkg/repositories/gorm_heartbeat_monitor_repository.go @@ -22,18 +22,74 @@ type gormHeartbeatMonitorRepository struct { db *gorm.DB } +// NewGormHeartbeatMonitorRepository creates the GORM version of the HeartbeatMonitorRepository +func NewGormHeartbeatMonitorRepository( + logger telemetry.Logger, + tracer telemetry.Tracer, + db *gorm.DB, +) HeartbeatMonitorRepository { + return &gormHeartbeatMonitorRepository{ + logger: logger.WithService(fmt.Sprintf("%T", &gormHeartbeatRepository{})), + tracer: tracer, + db: db, + } +} + +func (repository *gormHeartbeatMonitorRepository) DeleteAllForUser(ctx context.Context, userID entities.UserID) error { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + return executeWithRetry(func() error { + if err := repository.db.WithContext(ctx).Where("user_id = ?", userID).Delete(&entities.HeartbeatMonitor{}).Error; err != nil { + msg := fmt.Sprintf("cannot delete all [%T] for user with ID [%s]", &entities.HeartbeatMonitor{}, userID) + return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + return nil + }) +} + +// UpdatePhoneOnline updates the online status of a phone +func (repository *gormHeartbeatMonitorRepository) UpdatePhoneOnline(ctx context.Context, userID entities.UserID, monitorID uuid.UUID, isOnline bool) error { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + ctx, cancel := context.WithTimeout(ctx, dbOperationDuration) + defer cancel() + + err := executeWithRetry(func() error { + return repository.db. + Model(&entities.HeartbeatMonitor{}). + Where("id = ?", monitorID). + Where("user_id = ?", userID). + Updates(map[string]any{ + "phone_online": isOnline, + "updated_at": time.Now().UTC(), + }).Error + }) + if err != nil { + msg := fmt.Sprintf("cannot update heartbeat monitor ID [%s] for user [%s]", monitorID, userID) + return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + return nil +} + // UpdateQueueID updates the queueID of a monitor func (repository *gormHeartbeatMonitorRepository) UpdateQueueID(ctx context.Context, monitorID uuid.UUID, queueID string) error { ctx, span := repository.tracer.Start(ctx) defer span.End() - err := repository.db. - Model(&entities.HeartbeatMonitor{}). - Where("id = ?", monitorID). - Updates(map[string]any{ - "queue_id": queueID, - "updated_at": time.Now().UTC(), - }).Error + ctx, cancel := context.WithTimeout(ctx, dbOperationDuration) + defer cancel() + + err := executeWithRetry(func() error { + return repository.db. + Model(&entities.HeartbeatMonitor{}). + Where("id = ?", monitorID). + Updates(map[string]any{ + "queue_id": queueID, + "updated_at": time.Now().UTC(), + }).Error + }) if err != nil { msg := fmt.Sprintf("cannot update heartbeat monitor ID [%s]", monitorID) return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) @@ -45,10 +101,15 @@ func (repository *gormHeartbeatMonitorRepository) Delete(ctx context.Context, us ctx, span := repository.tracer.Start(ctx) defer span.End() - err := repository.db.WithContext(ctx). - Where("user_id = ?", userID). - Where("owner = ?", owner). - Delete(&entities.HeartbeatMonitor{}).Error + ctx, cancel := context.WithTimeout(ctx, dbOperationDuration) + defer cancel() + + err := executeWithRetry(func() error { + return repository.db.WithContext(ctx). + Where("user_id = ?", userID). + Where("owner = ?", owner). + Delete(&entities.HeartbeatMonitor{}).Error + }) if err != nil { msg := fmt.Sprintf("cannot delete heartbeat monitor with owner [%s] and userID [%s]", owner, userID) return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) @@ -57,27 +118,19 @@ func (repository *gormHeartbeatMonitorRepository) Delete(ctx context.Context, us return nil } -// NewGormHeartbeatMonitorRepository creates the GORM version of the HeartbeatMonitorRepository -func NewGormHeartbeatMonitorRepository( - logger telemetry.Logger, - tracer telemetry.Tracer, - db *gorm.DB, -) HeartbeatMonitorRepository { - return &gormHeartbeatMonitorRepository{ - logger: logger.WithService(fmt.Sprintf("%T", &gormHeartbeatRepository{})), - tracer: tracer, - db: db, - } -} - // Index entities.Message between 2 parties func (repository *gormHeartbeatMonitorRepository) Index(ctx context.Context, userID entities.UserID, owner string, params IndexParams) (*[]entities.Heartbeat, error) { ctx, span := repository.tracer.Start(ctx) defer span.End() + ctx, cancel := context.WithTimeout(ctx, dbOperationDuration) + defer cancel() + query := repository.db.WithContext(ctx).Where("user_id = ?", userID).Where("owner = ?", owner) heartbeats := new([]entities.Heartbeat) - if err := query.Order("timestamp DESC").Limit(params.Limit).Offset(params.Skip).Find(&heartbeats).Error; err != nil { + if err := executeWithRetry(func() error { + return query.Order("timestamp DESC").Limit(params.Limit).Offset(params.Skip).Find(&heartbeats).Error + }); err != nil { msg := fmt.Sprintf("cannot fetch heartbeats with owner [%s] and params [%+#v]", owner, params) return nil, repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) } @@ -90,7 +143,10 @@ func (repository *gormHeartbeatMonitorRepository) Store(ctx context.Context, hea ctx, span := repository.tracer.Start(ctx) defer span.End() - if err := repository.db.WithContext(ctx).Create(heartbeatMonitor).Error; err != nil { + ctx, cancel := context.WithTimeout(ctx, dbOperationDuration) + defer cancel() + + if err := executeWithRetry(func() error { return repository.db.WithContext(ctx).Create(heartbeatMonitor).Error }); err != nil { msg := fmt.Sprintf("cannot save heartbeatMonitor monitor with ID [%s]", heartbeatMonitor.ID) return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) } @@ -103,12 +159,16 @@ func (repository *gormHeartbeatMonitorRepository) Load(ctx context.Context, user ctx, span := repository.tracer.Start(ctx) defer span.End() - phone := new(entities.HeartbeatMonitor) - err := repository.db.WithContext(ctx). - Where("user_id = ?", userID). - Where("owner = ?", owner). - First(&phone).Error + ctx, cancel := context.WithTimeout(ctx, dbOperationDuration) + defer cancel() + phone := new(entities.HeartbeatMonitor) + err := executeWithRetry(func() error { + return repository.db.WithContext(ctx). + Where("user_id = ?", userID). + Where("owner = ?", owner). + First(&phone).Error + }) if errors.Is(err, gorm.ErrRecordNotFound) { msg := fmt.Sprintf("heartbeat monitor with userID [%s] and owner [%s] does not exist", userID, owner) return nil, repository.tracer.WrapErrorSpan(span, stacktrace.PropagateWithCode(err, ErrCodeNotFound, msg)) @@ -127,15 +187,20 @@ func (repository *gormHeartbeatMonitorRepository) Exists(ctx context.Context, us ctx, span := repository.tracer.Start(ctx) defer span.End() + ctx, cancel := context.WithTimeout(ctx, dbOperationDuration) + defer cancel() + var exists bool - err := repository.db.WithContext(ctx). - Model(&entities.HeartbeatMonitor{}). - Select("count(*) > 0"). - Where("user_id = ?", userID). - Where("id = ?", monitorID). - Find(&exists).Error + err := executeWithRetry(func() error { + return repository.db.WithContext(ctx). + Model(&entities.HeartbeatMonitor{}). + Select("count(*) > 0"). + Where("user_id = ?", userID). + Where("id = ?", monitorID). + Find(&exists).Error + }) if err != nil { - msg := fmt.Sprintf("cannot check if heartbeat monitor exists with userID [%s] and montiorID [%s]", userID, monitorID) + msg := fmt.Sprintf("cannot check if heartbeat monitor exists with userID [%s] and montior ID [%s]", userID, monitorID) return exists, repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) } diff --git a/api/pkg/repositories/gorm_heartbeat_repository.go b/api/pkg/repositories/gorm_heartbeat_repository.go index c3cb48df..5b7794e9 100644 --- a/api/pkg/repositories/gorm_heartbeat_repository.go +++ b/api/pkg/repositories/gorm_heartbeat_repository.go @@ -32,16 +32,36 @@ func NewGormHeartbeatRepository( } } +func (repository *gormHeartbeatRepository) DeleteAllForUser(ctx context.Context, userID entities.UserID) error { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + err := executeWithRetry(func() error { + return repository.db.WithContext(ctx).Where("user_id = ?", userID).Delete(&entities.Heartbeat{}).Error + }) + if err != nil { + msg := fmt.Sprintf("cannot delete all [%T] for user with ID [%s]", &entities.Heartbeat{}, userID) + return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + func (repository *gormHeartbeatRepository) Last(ctx context.Context, userID entities.UserID, owner string) (*entities.Heartbeat, error) { ctx, span := repository.tracer.Start(ctx) defer span.End() + ctx, cancel := context.WithTimeout(ctx, dbOperationDuration) + defer cancel() + heartbeat := new(entities.Heartbeat) - err := repository.db.WithContext(ctx). - Where("user_id = ?", userID). - Where("owner = ?", owner). - Order("timestamp DESC"). - First(&heartbeat).Error + err := executeWithRetry(func() error { + return repository.db.WithContext(ctx). + Where("user_id = ?", userID). + Where("owner = ?", owner). + Order("timestamp DESC"). + First(&heartbeat).Error + }) if errors.Is(err, gorm.ErrRecordNotFound) { msg := fmt.Sprintf("heartbeat with userID [%s] and owner [%s] does not exist", userID, owner) return nil, repository.tracer.WrapErrorSpan(span, stacktrace.PropagateWithCode(err, ErrCodeNotFound, msg)) @@ -60,6 +80,9 @@ func (repository *gormHeartbeatRepository) Index(ctx context.Context, userID ent ctx, span := repository.tracer.Start(ctx) defer span.End() + ctx, cancel := context.WithTimeout(ctx, dbOperationDuration) + defer cancel() + query := repository.db.WithContext(ctx).Where("user_id = ?", userID).Where("owner = ?", owner) if len(params.Query) > 0 { queryPattern := "%" + params.Query + "%" @@ -67,7 +90,10 @@ func (repository *gormHeartbeatRepository) Index(ctx context.Context, userID ent } heartbeats := new([]entities.Heartbeat) - if err := query.Order("timestamp DESC").Limit(params.Limit).Offset(params.Skip).Find(&heartbeats).Error; err != nil { + err := executeWithRetry(func() error { + return query.Order("timestamp DESC").Limit(params.Limit).Offset(params.Skip).Find(&heartbeats).Error + }) + if err != nil { msg := fmt.Sprintf("cannot fetch heartbeats with owner [%s] and params [%+#v]", owner, params) return nil, repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) } @@ -80,7 +106,10 @@ func (repository *gormHeartbeatRepository) Store(ctx context.Context, heartbeat ctx, span := repository.tracer.Start(ctx) defer span.End() - if err := repository.db.WithContext(ctx).Create(heartbeat).Error; err != nil { + ctx, cancel := context.WithTimeout(ctx, dbOperationDuration) + defer cancel() + + if err := executeWithRetry(func() error { return repository.db.WithContext(ctx).Create(heartbeat).Error }); err != nil { msg := fmt.Sprintf("cannot save heartbeat with ID [%s]", heartbeat.ID) return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) } diff --git a/api/pkg/repositories/gorm_integration_3cx_repository.go b/api/pkg/repositories/gorm_integration_3cx_repository.go index 9caedf8a..64b55257 100644 --- a/api/pkg/repositories/gorm_integration_3cx_repository.go +++ b/api/pkg/repositories/gorm_integration_3cx_repository.go @@ -31,6 +31,18 @@ func NewGormIntegration3CXRepository( } } +func (repository *gormIntegration3CxRepository) DeleteAllForUser(ctx context.Context, userID entities.UserID) error { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + if err := repository.db.WithContext(ctx).Where("user_id = ?", userID).Delete(&entities.Integration3CX{}).Error; err != nil { + msg := fmt.Sprintf("cannot delete all [%T] for user with ID [%s]", &entities.Integration3CX{}, userID) + return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + // Load an entities.Integration3CX based on the entities.UserID func (repository *gormIntegration3CxRepository) Load(ctx context.Context, userID entities.UserID) (*entities.Integration3CX, error) { ctx, span := repository.tracer.Start(ctx) diff --git a/api/pkg/repositories/gorm_message_repository.go b/api/pkg/repositories/gorm_message_repository.go index e499a69c..607af44e 100644 --- a/api/pkg/repositories/gorm_message_repository.go +++ b/api/pkg/repositories/gorm_message_repository.go @@ -35,6 +35,51 @@ func NewGormMessageRepository( } } +func (repository *gormMessageRepository) DeleteAllForUser(ctx context.Context, userID entities.UserID) error { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + if err := repository.db.WithContext(ctx).Where("user_id = ?", userID).Delete(&entities.Message{}).Error; err != nil { + msg := fmt.Sprintf("cannot delete all [%T] for user with ID [%s]", &entities.Message{}, userID) + return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + +// DeleteByOwnerAndContact deletes all the messages between and owner and a contact +func (repository *gormMessageRepository) DeleteByOwnerAndContact(ctx context.Context, userID entities.UserID, owner string, contact string) error { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + err := repository.db.WithContext(ctx). + Where("user_id = ?", userID). + Where("owner = ?", owner). + Where("contact = ?", contact). + Delete(&entities.Message{}). + Error + if err != nil { + msg := fmt.Sprintf("cannot delete messages between owner [%s] and contact [%s] for user with ID [%s]", owner, contact, userID) + return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + +// Delete a message by the ID +func (repository *gormMessageRepository) Delete(ctx context.Context, userID entities.UserID, messageID uuid.UUID) error { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + err := repository.db.WithContext(ctx).Where("user_id = ?", userID).Where("id = ?", messageID).Delete(&entities.Message{}).Error + if err != nil { + msg := fmt.Sprintf("cannot delete message with ID [%s] for user with ID [%s]", messageID, userID) + return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + // Index entities.Message between 2 parties func (repository *gormMessageRepository) Index(ctx context.Context, userID entities.UserID, owner string, contact string, params IndexParams) (*[]entities.Message, error) { ctx, span := repository.tracer.Start(ctx) @@ -59,6 +104,78 @@ func (repository *gormMessageRepository) Index(ctx context.Context, userID entit return messages, nil } +func (repository *gormMessageRepository) LastMessage(ctx context.Context, userID entities.UserID, owner string, contact string) (*entities.Message, error) { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + query := repository.db. + WithContext(ctx). + Where("user_id = ?", userID). + Where("owner = ?", owner). + Where("contact = ?", contact) + + message := new(entities.Message) + + err := query.Order("order_timestamp DESC").First(&message).Error + if errors.Is(err, gorm.ErrRecordNotFound) { + msg := fmt.Sprintf("cannot get last message for [%s] with owner [%s] and contact [%s]", userID, owner, contact) + return nil, repository.tracer.WrapErrorSpan(span, stacktrace.PropagateWithCode(err, ErrCodeNotFound, msg)) + } + + if err != nil { + msg := fmt.Sprintf("cannot get last message for [%s] with owner [%s] and contact [%s]", userID, owner, contact) + return nil, repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return message, nil +} + +func (repository *gormMessageRepository) Search(ctx context.Context, userID entities.UserID, owners []string, types []entities.MessageType, statuses []entities.MessageStatus, params IndexParams) ([]*entities.Message, error) { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + query := repository.db. + WithContext(ctx). + Where("user_id = ?", userID) + + if len(owners) > 0 { + query = query.Where("owner IN ?", owners) + } + if len(types) > 0 { + query = query.Where("type IN ?", types) + } + if len(statuses) > 0 { + query = query.Where("status IN ?", statuses) + } + + if len(params.Query) > 0 { + queryPattern := "%" + params.Query + "%" + subQuery := repository.db.Where("content ILIKE ?", queryPattern). + Or("contact ILIKE ?", queryPattern). + Or("failure_reason ILIKE ?", queryPattern). + Or("request_id ILIKE ?", queryPattern) + + if _, err := uuid.Parse(params.Query); err == nil { + subQuery = subQuery.Or("id = ?", params.Query) + } + + query = query.Where(subQuery) + } + + messages := make([]*entities.Message, 0, params.Limit) + err := query.Order(repository.order(params, "created_at")). + Limit(params.Limit). + Offset(params.Skip). + Find(&messages). + Error + if err != nil { + msg := fmt.Sprintf("cannot search messages with for user [%s] params [%+#v]", userID, params) + return nil, repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return messages, nil +} + // Store a new entities.Message func (repository *gormMessageRepository) Store(ctx context.Context, message *entities.Message) error { ctx, span := repository.tracer.Start(ctx) @@ -106,18 +223,23 @@ func (repository *gormMessageRepository) Update(ctx context.Context, message *en } // GetOutstanding fetches messages that still to be sent to the phone -func (repository *gormMessageRepository) GetOutstanding(ctx context.Context, userID entities.UserID, messageID uuid.UUID) (*entities.Message, error) { +func (repository *gormMessageRepository) GetOutstanding(ctx context.Context, userID entities.UserID, messageID uuid.UUID, phoneNumbers []string) (*entities.Message, error) { ctx, span := repository.tracer.Start(ctx) defer span.End() message := new(entities.Message) err := crdbgorm.ExecuteTx(ctx, repository.db, nil, func(tx *gorm.DB) error { - return tx.WithContext(ctx).Model(message). + query := tx.WithContext(ctx).Model(message). Clauses(clause.Returning{}). Where("user_id = ?", userID). - Where("id = ?", messageID). - Where(repository.db.Where("status = ?", entities.MessageStatusScheduled).Or("status = ?", entities.MessageStatusPending).Or("status = ?", entities.MessageStatusExpired)). + Where("id = ?", messageID) + + if len(phoneNumbers) > 0 { + query = query.Where("owner IN ?", phoneNumbers) + } + + return query.Where(repository.db.Where("status = ?", entities.MessageStatusScheduled).Or("status = ?", entities.MessageStatusPending).Or("status = ?", entities.MessageStatusExpired)). Update("status", entities.MessageStatusSending).Error }, ) @@ -138,3 +260,17 @@ func (repository *gormMessageRepository) GetOutstanding(ctx context.Context, use return message, nil } + +func (repository *gormMessageRepository) order(params IndexParams, defaultSortBy string) string { + sortBy := defaultSortBy + if len(params.SortBy) > 0 { + sortBy = params.SortBy + } + + direction := "ASC" + if params.SortDescending { + direction = "DESC" + } + + return fmt.Sprintf("%s %s", sortBy, direction) +} diff --git a/api/pkg/repositories/gorm_message_thread_repository.go b/api/pkg/repositories/gorm_message_thread_repository.go index 10ab9a8e..244e8b58 100644 --- a/api/pkg/repositories/gorm_message_thread_repository.go +++ b/api/pkg/repositories/gorm_message_thread_repository.go @@ -35,6 +35,53 @@ func NewGormMessageThreadRepository( } } +func (repository *gormMessageThreadRepository) DeleteAllForUser(ctx context.Context, userID entities.UserID) error { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + if err := repository.db.WithContext(ctx).Where("user_id = ?", userID).Delete(&entities.MessageThread{}).Error; err != nil { + msg := fmt.Sprintf("cannot delete all [%T] for user with ID [%s]", &entities.MessageThread{}, userID) + return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + +// Delete the message thread for a user +func (repository *gormMessageThreadRepository) Delete(ctx context.Context, userID entities.UserID, messageThreadID uuid.UUID) error { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + err := repository.db.WithContext(ctx).Where("user_id = ?", userID).Where("id = ?", messageThreadID).Delete(&entities.MessageThread{}).Error + if err != nil { + msg := fmt.Sprintf("cannot delete message thread with ID [%s] for user with ID [%s]", messageThreadID, userID) + return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + +// UpdateAfterDeletedMessage updates a thread after the original message has been deleted +func (repository *gormMessageThreadRepository) UpdateAfterDeletedMessage(ctx context.Context, userID entities.UserID, messageID uuid.UUID) error { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + err := repository.db.WithContext(ctx).Model(&entities.MessageThread{}). + Where("user_id = ?", userID). + Where("last_message_id = ?", messageID). + Updates(map[string]any{ + "last_message_id": nil, + "last_message_content": nil, + "status": entities.MessageStatusDeleted, + }).Error + if err != nil { + msg := fmt.Sprintf("cannot update thread after message is deleted with userID [%s] and messageID [%s]", userID, messageID) + return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + // Store a new entities.MessageThread func (repository *gormMessageThreadRepository) Store(ctx context.Context, thread *entities.MessageThread) error { ctx, span := repository.tracer.Start(ctx) diff --git a/api/pkg/repositories/gorm_phone_api_key_repository.go b/api/pkg/repositories/gorm_phone_api_key_repository.go new file mode 100644 index 00000000..3c306f5a --- /dev/null +++ b/api/pkg/repositories/gorm_phone_api_key_repository.go @@ -0,0 +1,238 @@ +package repositories + +import ( + "context" + "errors" + "fmt" + "time" + + "github.com/cockroachdb/cockroach-go/v2/crdb/crdbgorm" + + "github.com/NdoleStudio/httpsms/pkg/entities" + "github.com/NdoleStudio/httpsms/pkg/telemetry" + "github.com/dgraph-io/ristretto/v2" + "github.com/google/uuid" + "github.com/palantir/stacktrace" + "gorm.io/gorm" +) + +// gormPhoneAPIKeyRepository is responsible for persisting entities.PhoneAPIKey +type gormPhoneAPIKeyRepository struct { + logger telemetry.Logger + tracer telemetry.Tracer + cache *ristretto.Cache[string, entities.AuthContext] + db *gorm.DB +} + +// NewGormPhoneAPIKeyRepository creates the GORM version of the PhoneAPIKeyRepository +func NewGormPhoneAPIKeyRepository( + logger telemetry.Logger, + tracer telemetry.Tracer, + db *gorm.DB, + cache *ristretto.Cache[string, entities.AuthContext], +) PhoneAPIKeyRepository { + return &gormPhoneAPIKeyRepository{ + logger: logger.WithService(fmt.Sprintf("%T", &gormPhoneAPIKeyRepository{})), + tracer: tracer, + cache: cache, + db: db, + } +} + +func (repository *gormPhoneAPIKeyRepository) RemovePhoneByID(ctx context.Context, userID entities.UserID, phoneID uuid.UUID, phoneNumber string) error { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + query := ` +UPDATE phone_api_keys +SET phone_ids = array_remove(phone_ids, ?), + phone_numbers = array_remove(phone_numbers, ?) +WHERE user_id = ? AND array_position(phone_ids, ?) IS NOT NULL; +` + err := repository.db.WithContext(ctx). + Exec(query, phoneID, phoneNumber, userID, phoneID). + Error + if err != nil { + msg := fmt.Sprintf("cannot remove phone with ID [%s] and number [%s] for user with ID [%s] ", phoneID, phoneNumber, userID) + return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + repository.cache.Clear() + return nil +} + +// Load an entities.PhoneAPIKey based on the entities.UserID +func (repository *gormPhoneAPIKeyRepository) Load(ctx context.Context, userID entities.UserID, phoneAPIKeyID uuid.UUID) (*entities.PhoneAPIKey, error) { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + phoneAPIKey := new(entities.PhoneAPIKey) + err := repository.db.WithContext(ctx).Where("user_id = ?", userID).Where("id = ?", phoneAPIKeyID).First(&phoneAPIKey).Error + if errors.Is(err, gorm.ErrRecordNotFound) { + msg := fmt.Sprintf("[%T] with ID [%s] for user with ID [%s] does not exist", phoneAPIKey, phoneAPIKeyID, userID) + return nil, repository.tracer.WrapErrorSpan(span, stacktrace.PropagateWithCode(err, ErrCodeNotFound, msg)) + } + + if err != nil { + msg := fmt.Sprintf("cannot load [%T] with ID [%s] for user with ID [%s]", phoneAPIKey, phoneAPIKeyID, userID) + return nil, repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return phoneAPIKey, nil +} + +func (repository *gormPhoneAPIKeyRepository) Create(ctx context.Context, phoneAPIKey *entities.PhoneAPIKey) error { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + if err := repository.db.WithContext(ctx).Create(phoneAPIKey).Error; err != nil { + msg := fmt.Sprintf("cannot save phone API key with ID [%s] for user with ID [%s]", phoneAPIKey.ID, phoneAPIKey.UserID) + return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + +func (repository *gormPhoneAPIKeyRepository) LoadAuthContext(ctx context.Context, apiKey string) (entities.AuthContext, error) { + ctx, span, ctxLogger := repository.tracer.StartWithLogger(ctx, repository.logger) + defer span.End() + + if authContext, found := repository.cache.Get(apiKey); found { + return authContext, nil + } + + phoneAPIKey := new(entities.PhoneAPIKey) + err := repository.db.WithContext(ctx).Where("api_key = ?", apiKey).First(phoneAPIKey).Error + if errors.Is(err, gorm.ErrRecordNotFound) { + msg := fmt.Sprintf("phone api key [%s] does not exist", apiKey) + return entities.AuthContext{}, repository.tracer.WrapErrorSpan(span, stacktrace.PropagateWithCode(err, ErrCodeNotFound, msg)) + } + + if err != nil { + msg := fmt.Sprintf("cannot load phone api key [%s]", apiKey) + return entities.AuthContext{}, repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + authUser := entities.AuthContext{ + ID: phoneAPIKey.UserID, + Email: phoneAPIKey.UserEmail, + PhoneAPIKeyID: &phoneAPIKey.ID, + PhoneNumbers: phoneAPIKey.PhoneNumbers, + } + + if result := repository.cache.SetWithTTL(apiKey, authUser, 1, 15*time.Second); !result { + msg := fmt.Sprintf("cannot cache [%T] with ID [%s] and result [%t]", authUser, phoneAPIKey.ID, result) + ctxLogger.Error(repository.tracer.WrapErrorSpan(span, stacktrace.NewError(msg))) + } + + return authUser, nil +} + +func (repository *gormPhoneAPIKeyRepository) Index(ctx context.Context, userID entities.UserID, params IndexParams) ([]*entities.PhoneAPIKey, error) { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + query := repository.db.WithContext(ctx).Where("user_id = ?", userID) + if len(params.Query) > 0 { + queryPattern := "%" + params.Query + "%" + query.Where("name ILIKE ?", queryPattern) + } + + phoneAPIKeys := new([]*entities.PhoneAPIKey) + if err := query.Order("created_at DESC").Limit(params.Limit).Offset(params.Skip).Find(phoneAPIKeys).Error; err != nil { + msg := fmt.Sprintf("cannot fetch phone API Keys with userID [%s] and params [%+#v]", userID, params) + return nil, repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return *phoneAPIKeys, nil +} + +func (repository *gormPhoneAPIKeyRepository) Delete(ctx context.Context, phoneAPIKey *entities.PhoneAPIKey) error { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + err := repository.db.WithContext(ctx).Delete(phoneAPIKey).Error + if err != nil { + msg := fmt.Sprintf("cannot delete phone API key with ID [%s] and userID [%s]", phoneAPIKey.ID, phoneAPIKey.UserID) + return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + repository.cache.Del(phoneAPIKey.APIKey) + + return nil +} + +func (repository *gormPhoneAPIKeyRepository) AddPhone(ctx context.Context, phoneAPIKey *entities.PhoneAPIKey, phone *entities.Phone) error { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + err := crdbgorm.ExecuteTx(ctx, repository.db, nil, func(tx *gorm.DB) error { + query := ` +UPDATE phone_api_keys +SET phone_ids = array_remove(phone_ids, ?), + phone_numbers = array_remove(phone_numbers, ?) +WHERE user_id = ?; +` + err := tx.WithContext(ctx). + Exec(query, phone.ID, phone.PhoneNumber, phone.UserID). + Error + if err != nil { + msg := fmt.Sprintf("cannot remove phone with ID [%s] from API Key with ID [%s] for user with ID [%s]", phone.ID, phoneAPIKey.ID, phone.UserID) + return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + query = ` +UPDATE phone_api_keys +SET phone_ids = array_append(phone_ids, ?), + phone_numbers = array_append(phone_numbers, ?) +WHERE array_position(phone_ids, ?) IS NULL AND id = ?; +` + err = tx.WithContext(ctx). + Exec(query, phone.ID, phone.PhoneNumber, phone.ID, phoneAPIKey.ID). + Error + if err != nil { + msg := fmt.Sprintf("cannot add [%T] with ID [%s] from API Key with ID [%s] for user with ID [%s]", phone, phone.ID, phoneAPIKey.ID, phone.UserID) + return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil + }) + if err != nil { + msg := fmt.Sprintf("cannot add [%T] with ID [%s] from API Key with ID [%s] for user with ID [%s]", phone, phone.ID, phoneAPIKey.ID, phone.UserID) + return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + repository.cache.Clear() + return nil +} + +func (repository *gormPhoneAPIKeyRepository) RemovePhone(ctx context.Context, phoneAPIKey *entities.PhoneAPIKey, phone *entities.Phone) error { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + query := ` +UPDATE phone_api_keys +SET phone_ids = array_remove(phone_ids, ?), + phone_numbers = array_remove(phone_numbers, ?) +WHERE id = ?; +` + err := repository.db.WithContext(ctx). + Exec(query, phone.ID, phone.PhoneNumber, phoneAPIKey.ID). + Error + if err != nil { + msg := fmt.Sprintf("cannot remove phone with ID [%s] from phone API key with ID [%s]", phone.ID, phoneAPIKey.ID) + return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + +func (repository *gormPhoneAPIKeyRepository) DeleteAllForUser(ctx context.Context, userID entities.UserID) error { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + if err := repository.db.WithContext(ctx).Where("user_id = ?", userID).Delete(&entities.PhoneAPIKey{}).Error; err != nil { + msg := fmt.Sprintf("cannot delete all [%T] for user with ID [%s]", &entities.PhoneAPIKey{}, userID) + return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} diff --git a/api/pkg/repositories/gorm_phone_notification_repository.go b/api/pkg/repositories/gorm_phone_notification_repository.go index f005eec1..f491e4b3 100644 --- a/api/pkg/repositories/gorm_phone_notification_repository.go +++ b/api/pkg/repositories/gorm_phone_notification_repository.go @@ -35,8 +35,20 @@ func NewGormPhoneNotificationRepository( } } +func (repository *gormPhoneNotificationRepository) DeleteAllForUser(ctx context.Context, userID entities.UserID) error { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + if err := repository.db.WithContext(ctx).Where("user_id = ?", userID).Delete(&entities.PhoneNotification{}).Error; err != nil { + msg := fmt.Sprintf("cannot delete all [%T] for user with ID [%s]", &entities.PhoneNotification{}, userID) + return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + // UpdateStatus of an entities.PhoneNotification -func (repository gormPhoneNotificationRepository) UpdateStatus(ctx context.Context, notificationID uuid.UUID, status entities.PhoneNotificationStatus) error { +func (repository *gormPhoneNotificationRepository) UpdateStatus(ctx context.Context, notificationID uuid.UUID, status entities.PhoneNotificationStatus) error { ctx, span := repository.tracer.Start(ctx) defer span.End() @@ -54,7 +66,7 @@ func (repository gormPhoneNotificationRepository) UpdateStatus(ctx context.Conte } // Schedule a notification to be sent in the future -func (repository gormPhoneNotificationRepository) Schedule(ctx context.Context, messagesPerMinute uint, notification *entities.PhoneNotification) error { +func (repository *gormPhoneNotificationRepository) Schedule(ctx context.Context, messagesPerMinute uint, notification *entities.PhoneNotification) error { ctx, span := repository.tracer.Start(ctx) defer span.End() diff --git a/api/pkg/repositories/gorm_phone_repository.go b/api/pkg/repositories/gorm_phone_repository.go index 9d37c731..55b6fb3f 100644 --- a/api/pkg/repositories/gorm_phone_repository.go +++ b/api/pkg/repositories/gorm_phone_repository.go @@ -4,9 +4,11 @@ import ( "context" "errors" "fmt" + "time" "github.com/NdoleStudio/httpsms/pkg/entities" "github.com/NdoleStudio/httpsms/pkg/telemetry" + "github.com/dgraph-io/ristretto/v2" "github.com/google/uuid" "github.com/palantir/stacktrace" "gorm.io/gorm" @@ -16,6 +18,7 @@ import ( type gormPhoneRepository struct { logger telemetry.Logger tracer telemetry.Tracer + cache *ristretto.Cache[string, *entities.Phone] db *gorm.DB } @@ -24,14 +27,29 @@ func NewGormPhoneRepository( logger telemetry.Logger, tracer telemetry.Tracer, db *gorm.DB, + cache *ristretto.Cache[string, *entities.Phone], ) PhoneRepository { return &gormPhoneRepository{ logger: logger.WithService(fmt.Sprintf("%T", &gormPhoneRepository{})), tracer: tracer, db: db, + cache: cache, } } +func (repository *gormPhoneRepository) DeleteAllForUser(ctx context.Context, userID entities.UserID) error { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + if err := repository.db.WithContext(ctx).Where("user_id = ?", userID).Delete(&entities.Phone{}).Error; err != nil { + msg := fmt.Sprintf("cannot delete all [%T] for user with ID [%s]", &entities.Phone{}, userID) + return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + repository.cache.Clear() + return nil +} + // LoadByID loads a phone by ID func (repository *gormPhoneRepository) LoadByID(ctx context.Context, userID entities.UserID, phoneID uuid.UUID) (*entities.Phone, error) { ctx, span := repository.tracer.Start(ctx) @@ -69,31 +87,45 @@ func (repository *gormPhoneRepository) Delete(ctx context.Context, userID entiti return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) } + repository.cache.Clear() return nil } // Save a new entities.Phone func (repository *gormPhoneRepository) Save(ctx context.Context, phone *entities.Phone) error { - ctx, span := repository.tracer.Start(ctx) + ctx, span, ctxLogger := repository.tracer.StartWithLogger(ctx, repository.logger) defer span.End() - err := repository.db. - WithContext(ctx). - Save(phone). - Error - if err != nil && !errors.Is(err, gorm.ErrDuplicatedKey) { + err := repository.db.WithContext(ctx).Save(phone).Error + if errors.Is(err, gorm.ErrDuplicatedKey) { + ctxLogger.Info(fmt.Sprintf("phone with user [%s] and number[%s] already exists", phone.UserID, phone.PhoneNumber)) + loadedPhone, err := repository.Load(ctx, phone.UserID, phone.PhoneNumber) + if err != nil { + msg := fmt.Sprintf("cannot load phone for user [%s] and number [%s]", phone.UserID, phone.PhoneNumber) + return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + *phone = *loadedPhone + return nil + } + + if err != nil { msg := fmt.Sprintf("cannot save phone with ID [%s]", phone.ID) return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) } + repository.cache.Del(repository.getCacheKey(phone.UserID, phone.PhoneNumber)) return nil } // Load a phone based on entities.UserID and phoneNumber func (repository *gormPhoneRepository) Load(ctx context.Context, userID entities.UserID, phoneNumber string) (*entities.Phone, error) { - ctx, span := repository.tracer.Start(ctx) + ctx, span, ctxLogger := repository.tracer.StartWithLogger(ctx, repository.logger) defer span.End() + if phone, found := repository.cache.Get(repository.getCacheKey(userID, phoneNumber)); found { + return phone, nil + } + phone := new(entities.Phone) err := repository.db.WithContext(ctx).Where("user_id = ?", userID).Where("phone_number = ?", phoneNumber).First(phone).Error if errors.Is(err, gorm.ErrRecordNotFound) { @@ -106,6 +138,11 @@ func (repository *gormPhoneRepository) Load(ctx context.Context, userID entities return nil, repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) } + if result := repository.cache.SetWithTTL(repository.getCacheKey(userID, phoneNumber), phone, 1, 30*time.Minute); !result { + msg := fmt.Sprintf("cannot cache [%T] with ID [%s] and result [%t]", phone, phone.ID, result) + ctxLogger.Error(repository.tracer.WrapErrorSpan(span, stacktrace.NewError(msg))) + } + return phone, nil } @@ -127,3 +164,7 @@ func (repository *gormPhoneRepository) Index(ctx context.Context, userID entitie return phones, nil } + +func (repository *gormPhoneRepository) getCacheKey(userID entities.UserID, phoneNumber string) string { + return fmt.Sprintf("user:%s:phone:%s", userID, phoneNumber) +} diff --git a/api/pkg/repositories/gorm_user_repository.go b/api/pkg/repositories/gorm_user_repository.go index 0c79405f..e31b2848 100644 --- a/api/pkg/repositories/gorm_user_repository.go +++ b/api/pkg/repositories/gorm_user_repository.go @@ -8,8 +8,10 @@ import ( "fmt" "time" + "gorm.io/gorm/clause" + "github.com/cockroachdb/cockroach-go/v2/crdb/crdbgorm" - "github.com/dgraph-io/ristretto" + "github.com/dgraph-io/ristretto/v2" "github.com/NdoleStudio/httpsms/pkg/entities" "github.com/NdoleStudio/httpsms/pkg/telemetry" @@ -21,7 +23,7 @@ import ( type gormUserRepository struct { logger telemetry.Logger tracer telemetry.Tracer - cache *ristretto.Cache + cache *ristretto.Cache[string, entities.AuthContext] db *gorm.DB } @@ -29,7 +31,7 @@ type gormUserRepository struct { func NewGormUserRepository( logger telemetry.Logger, tracer telemetry.Tracer, - cache *ristretto.Cache, + cache *ristretto.Cache[string, entities.AuthContext], db *gorm.DB, ) UserRepository { return &gormUserRepository{ @@ -40,6 +42,45 @@ func NewGormUserRepository( } } +// Delete deletes a user +func (repository *gormUserRepository) Delete(ctx context.Context, user *entities.User) error { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + if err := repository.db.WithContext(ctx).Delete(user).Error; err != nil { + msg := fmt.Sprintf("cannot delete user with ID [%s]", user.ID) + return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + +func (repository *gormUserRepository) RotateAPIKey(ctx context.Context, userID entities.UserID) (*entities.User, error) { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + apiKey, err := repository.generateAPIKey(64) + if err != nil { + return nil, stacktrace.Propagate(err, fmt.Sprintf("cannot generate apiKey for user [%s]", userID)) + } + + user := new(entities.User) + err = crdbgorm.ExecuteTx(ctx, repository.db, nil, + func(tx *gorm.DB) error { + return tx.WithContext(ctx).Model(user). + Clauses(clause.Returning{}). + Where("id = ?", userID). + Update("api_key", "uk_"+apiKey).Error + }, + ) + if errors.Is(err, gorm.ErrRecordNotFound) { + msg := fmt.Sprintf("user with ID [%s] does not exist", userID) + return nil, repository.tracer.WrapErrorSpan(span, stacktrace.PropagateWithCode(err, ErrCodeNotFound, msg)) + } + + return user, nil +} + func (repository *gormUserRepository) LoadBySubscriptionID(ctx context.Context, subscriptionID string) (*entities.User, error) { ctx, span := repository.tracer.Start(ctx) defer span.End() @@ -62,6 +103,28 @@ func (repository *gormUserRepository) LoadBySubscriptionID(ctx context.Context, return user, nil } +func (repository *gormUserRepository) LoadByEmail(ctx context.Context, email string) (*entities.User, error) { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + user := new(entities.User) + err := repository.db.WithContext(ctx). + Where("email = ?", email). + First(user). + Error + if errors.Is(err, gorm.ErrRecordNotFound) { + msg := fmt.Sprintf("user with email [%s] does not exist", email) + return nil, repository.tracer.WrapErrorSpan(span, stacktrace.PropagateWithCode(err, ErrCodeNotFound, msg)) + } + + if err != nil { + msg := fmt.Sprintf("cannot load user with email ID [%s]", email) + return nil, repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return user, nil +} + func (repository *gormUserRepository) Store(ctx context.Context, user *entities.User) error { ctx, span := repository.tracer.Start(ctx) defer span.End() @@ -86,28 +149,31 @@ func (repository *gormUserRepository) Update(ctx context.Context, user *entities return nil } -func (repository *gormUserRepository) LoadAuthUser(ctx context.Context, apiKey string) (entities.AuthUser, error) { +func (repository *gormUserRepository) LoadAuthContext(ctx context.Context, apiKey string) (entities.AuthContext, error) { ctx, span, ctxLogger := repository.tracer.StartWithLogger(ctx, repository.logger) defer span.End() if authUser, found := repository.cache.Get(apiKey); found { - ctxLogger.Info(fmt.Sprintf("cache hit for user with ID [%s]", authUser.(entities.AuthUser).ID)) - return authUser.(entities.AuthUser), nil + if authUser.IsNoop() { + return authUser, repository.tracer.WrapErrorSpan(span, stacktrace.NewError(fmt.Sprintf("user with api key [%s] does not exist", apiKey))) + } + return authUser, nil } user := new(entities.User) err := repository.db.WithContext(ctx).Where("api_key = ?", apiKey).First(user).Error if errors.Is(err, gorm.ErrRecordNotFound) { + repository.cache.SetWithTTL(apiKey, entities.AuthContext{}, 1, 2*time.Hour) msg := fmt.Sprintf("user with api key [%s] does not exist", apiKey) - return entities.AuthUser{}, repository.tracer.WrapErrorSpan(span, stacktrace.PropagateWithCode(err, ErrCodeNotFound, msg)) + return entities.AuthContext{}, repository.tracer.WrapErrorSpan(span, stacktrace.PropagateWithCode(err, ErrCodeNotFound, msg)) } if err != nil { msg := fmt.Sprintf("cannot load user with api key [%s]", apiKey) - return entities.AuthUser{}, repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + return entities.AuthContext{}, repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) } - authUser := entities.AuthUser{ + authUser := entities.AuthContext{ ID: user.ID, Email: user.Email, } @@ -139,7 +205,7 @@ func (repository *gormUserRepository) Load(ctx context.Context, userID entities. return user, nil } -func (repository *gormUserRepository) LoadOrStore(ctx context.Context, authUser entities.AuthUser) (*entities.User, bool, error) { +func (repository *gormUserRepository) LoadOrStore(ctx context.Context, authUser entities.AuthContext) (*entities.User, bool, error) { ctx, span := repository.tracer.Start(ctx) defer span.End() @@ -150,13 +216,13 @@ func (repository *gormUserRepository) LoadOrStore(ctx context.Context, authUser apiKey, err := repository.generateAPIKey(64) if err != nil { - return nil, false, stacktrace.Propagate(err, "cannot generate apiKey") + return nil, false, stacktrace.Propagate(err, fmt.Sprintf("cannot generate apiKey for user [%s]", authUser.ID)) } user = &entities.User{ ID: authUser.ID, Email: authUser.Email, - APIKey: apiKey, + APIKey: "uk_" + apiKey, SubscriptionName: entities.SubscriptionNameFree, CreatedAt: time.Now().UTC(), UpdatedAt: time.Now().UTC(), diff --git a/api/pkg/repositories/gorm_webhook_repository.go b/api/pkg/repositories/gorm_webhook_repository.go index 2aae5734..880ad34f 100644 --- a/api/pkg/repositories/gorm_webhook_repository.go +++ b/api/pkg/repositories/gorm_webhook_repository.go @@ -32,6 +32,18 @@ func NewGormWebhookRepository( } } +func (repository *gormWebhookRepository) DeleteAllForUser(ctx context.Context, userID entities.UserID) error { + ctx, span := repository.tracer.Start(ctx) + defer span.End() + + if err := repository.db.WithContext(ctx).Where("user_id = ?", userID).Delete(&entities.Webhook{}).Error; err != nil { + msg := fmt.Sprintf("cannot delete all [%T] for user with ID [%s]", &entities.Webhook{}, userID) + return repository.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + func (repository *gormWebhookRepository) Save(ctx context.Context, webhook *entities.Webhook) error { ctx, span := repository.tracer.Start(ctx) defer span.End() diff --git a/api/pkg/repositories/heartbeat_monitor_repository.go b/api/pkg/repositories/heartbeat_monitor_repository.go index 5c820488..ddcde94d 100644 --- a/api/pkg/repositories/heartbeat_monitor_repository.go +++ b/api/pkg/repositories/heartbeat_monitor_repository.go @@ -24,4 +24,10 @@ type HeartbeatMonitorRepository interface { // Delete an entities.HeartbeatMonitor Delete(ctx context.Context, userID entities.UserID, phoneNumber string) error + + // UpdatePhoneOnline updates the phone online status of a monitor + UpdatePhoneOnline(ctx context.Context, userID entities.UserID, monitorID uuid.UUID, online bool) error + + // DeleteAllForUser deletes all entities.HeartbeatMonitor for a user + DeleteAllForUser(ctx context.Context, userID entities.UserID) error } diff --git a/api/pkg/repositories/heartbeat_repository.go b/api/pkg/repositories/heartbeat_repository.go index 9bc8f665..cf35cd9c 100644 --- a/api/pkg/repositories/heartbeat_repository.go +++ b/api/pkg/repositories/heartbeat_repository.go @@ -16,4 +16,7 @@ type HeartbeatRepository interface { // Last entities.Heartbeat returns the last heartbeat Last(ctx context.Context, userID entities.UserID, owner string) (*entities.Heartbeat, error) + + // DeleteAllForUser deletes all entities.Heartbeat for a user + DeleteAllForUser(ctx context.Context, userID entities.UserID) error } diff --git a/api/pkg/repositories/integration_3cx_repository.go b/api/pkg/repositories/integration_3cx_repository.go index cd6f533c..09cfea29 100644 --- a/api/pkg/repositories/integration_3cx_repository.go +++ b/api/pkg/repositories/integration_3cx_repository.go @@ -13,4 +13,7 @@ type Integration3CxRepository interface { // Load an entities.Integration3CX based on the entities.UserID Load(ctx context.Context, userID entities.UserID) (*entities.Integration3CX, error) + + // DeleteAllForUser deletes all entities.Integration3CX for a user + DeleteAllForUser(ctx context.Context, userID entities.UserID) error } diff --git a/api/pkg/repositories/memory_attachment_repository.go b/api/pkg/repositories/memory_attachment_repository.go new file mode 100644 index 00000000..65eadf2f --- /dev/null +++ b/api/pkg/repositories/memory_attachment_repository.go @@ -0,0 +1,60 @@ +package repositories + +import ( + "context" + "fmt" + "sync" + + "github.com/NdoleStudio/httpsms/pkg/telemetry" + "github.com/palantir/stacktrace" +) + +// MemoryAttachmentRepository stores attachments in memory +type MemoryAttachmentRepository struct { + logger telemetry.Logger + tracer telemetry.Tracer + data sync.Map +} + +// NewMemoryAttachmentRepository creates a new MemoryAttachmentRepository +func NewMemoryAttachmentRepository( + logger telemetry.Logger, + tracer telemetry.Tracer, +) *MemoryAttachmentRepository { + return &MemoryAttachmentRepository{ + logger: logger.WithService(fmt.Sprintf("%T", &MemoryAttachmentRepository{})), + tracer: tracer, + } +} + +// Upload stores attachment data at the given path +func (s *MemoryAttachmentRepository) Upload(ctx context.Context, path string, data []byte, _ string) error { + _, span, ctxLogger := s.tracer.StartWithLogger(ctx, s.logger) + defer span.End() + + s.data.Store(path, data) + ctxLogger.Info(fmt.Sprintf("stored attachment at path [%s] with size [%d]", path, len(data))) + return nil +} + +// Download retrieves attachment data from the given path +func (s *MemoryAttachmentRepository) Download(ctx context.Context, path string) ([]byte, error) { + _, span, _ := s.tracer.StartWithLogger(ctx, s.logger) + defer span.End() + + value, ok := s.data.Load(path) + if !ok { + return nil, s.tracer.WrapErrorSpan(span, stacktrace.NewErrorWithCode(ErrCodeNotFound, fmt.Sprintf("attachment not found at path [%s]", path))) + } + return value.([]byte), nil +} + +// Delete removes an attachment at the given path +func (s *MemoryAttachmentRepository) Delete(ctx context.Context, path string) error { + _, span, ctxLogger := s.tracer.StartWithLogger(ctx, s.logger) + defer span.End() + + s.data.Delete(path) + ctxLogger.Info(fmt.Sprintf("deleted attachment at path [%s]", path)) + return nil +} diff --git a/api/pkg/repositories/message_repository.go b/api/pkg/repositories/message_repository.go index 4277cfcb..3ad70015 100644 --- a/api/pkg/repositories/message_repository.go +++ b/api/pkg/repositories/message_repository.go @@ -21,6 +21,21 @@ type MessageRepository interface { // Index entities.Message between 2 phone numbers Index(ctx context.Context, userID entities.UserID, owner string, contact string, params IndexParams) (*[]entities.Message, error) + // LastMessage fetches the last message between an owner and a contact + LastMessage(ctx context.Context, userID entities.UserID, owner string, contact string) (*entities.Message, error) + + // Search entities.Message for a user + Search(ctx context.Context, userID entities.UserID, owners []string, types []entities.MessageType, statuses []entities.MessageStatus, params IndexParams) ([]*entities.Message, error) + // GetOutstanding fetches an entities.Message which is outstanding - GetOutstanding(ctx context.Context, userID entities.UserID, messageID uuid.UUID) (*entities.Message, error) + GetOutstanding(ctx context.Context, userID entities.UserID, messageID uuid.UUID, phoneNumbers []string) (*entities.Message, error) + + // Delete an entities.Message by ID + Delete(ctx context.Context, userID entities.UserID, messageID uuid.UUID) error + + // DeleteByOwnerAndContact deletes messages between an owner and a contact + DeleteByOwnerAndContact(ctx context.Context, userID entities.UserID, owner string, contact string) error + + // DeleteAllForUser deletes all entities.Message for a user + DeleteAllForUser(ctx context.Context, userID entities.UserID) error } diff --git a/api/pkg/repositories/message_thread_repository.go b/api/pkg/repositories/message_thread_repository.go index c695e976..542fd519 100644 --- a/api/pkg/repositories/message_thread_repository.go +++ b/api/pkg/repositories/message_thread_repository.go @@ -24,4 +24,13 @@ type MessageThreadRepository interface { // Index message threads for an owner Index(ctx context.Context, userID entities.UserID, owner string, archived bool, params IndexParams) (*[]entities.MessageThread, error) + + // UpdateAfterDeletedMessage updates a thread after the original message has been deleted + UpdateAfterDeletedMessage(ctx context.Context, userID entities.UserID, messageID uuid.UUID) error + + // Delete an entities.MessageThread by ID + Delete(ctx context.Context, userID entities.UserID, messageThreadID uuid.UUID) error + + // DeleteAllForUser deletes all entities.MessageThread for a user + DeleteAllForUser(ctx context.Context, userID entities.UserID) error } diff --git a/api/pkg/repositories/phone_api_key_repository.go b/api/pkg/repositories/phone_api_key_repository.go new file mode 100644 index 00000000..8894e4ac --- /dev/null +++ b/api/pkg/repositories/phone_api_key_repository.go @@ -0,0 +1,39 @@ +package repositories + +import ( + "context" + + "github.com/google/uuid" + + "github.com/NdoleStudio/httpsms/pkg/entities" +) + +// PhoneAPIKeyRepository loads and persists an entities.PhoneAPIKey +type PhoneAPIKeyRepository interface { + // Create a new entities.PhoneAPIKey + Create(ctx context.Context, phone *entities.PhoneAPIKey) error + + // Load an entities.PhoneAPIKey by userID and phoneAPIKeyID + Load(ctx context.Context, userID entities.UserID, phoneAPIKeyID uuid.UUID) (*entities.PhoneAPIKey, error) + + // LoadAuthContext fetches an entities.AuthContext by apiKey + LoadAuthContext(ctx context.Context, apiKey string) (entities.AuthContext, error) + + // Index entities.PhoneAPIKey of a user + Index(ctx context.Context, userID entities.UserID, params IndexParams) ([]*entities.PhoneAPIKey, error) + + // Delete an entities.PhoneAPIKey + Delete(ctx context.Context, phoneAPIKey *entities.PhoneAPIKey) error + + // AddPhone adds an entities.Phone to an entities.PhoneAPIKey + AddPhone(ctx context.Context, phoneAPIKey *entities.PhoneAPIKey, phone *entities.Phone) error + + // RemovePhone removes an entities.Phone to an entities.PhoneAPIKey + RemovePhone(ctx context.Context, phoneAPIKey *entities.PhoneAPIKey, phone *entities.Phone) error + + // DeleteAllForUser deletes all entities.PhoneAPIKey for a user + DeleteAllForUser(ctx context.Context, userID entities.UserID) error + + // RemovePhoneByID removes a phone by ID and phone number + RemovePhoneByID(ctx context.Context, userID entities.UserID, phoneID uuid.UUID, phoneNumber string) error +} diff --git a/api/pkg/repositories/phone_notification_repository.go b/api/pkg/repositories/phone_notification_repository.go index 299e165d..87f78490 100644 --- a/api/pkg/repositories/phone_notification_repository.go +++ b/api/pkg/repositories/phone_notification_repository.go @@ -15,4 +15,7 @@ type PhoneNotificationRepository interface { // UpdateStatus of a notification UpdateStatus(ctx context.Context, notificationID uuid.UUID, status entities.PhoneNotificationStatus) error + + // DeleteAllForUser deletes all entities.PhoneNotification for a user + DeleteAllForUser(ctx context.Context, userID entities.UserID) error } diff --git a/api/pkg/repositories/phone_repository.go b/api/pkg/repositories/phone_repository.go index bd124450..c9e82b98 100644 --- a/api/pkg/repositories/phone_repository.go +++ b/api/pkg/repositories/phone_repository.go @@ -24,4 +24,7 @@ type PhoneRepository interface { // Delete an entities.Phone Delete(ctx context.Context, userID entities.UserID, phoneID uuid.UUID) error + + // DeleteAllForUser deletes all entities.Phone for a user + DeleteAllForUser(ctx context.Context, userID entities.UserID) error } diff --git a/api/pkg/repositories/repository.go b/api/pkg/repositories/repository.go index d72a5609..4a3e90dc 100644 --- a/api/pkg/repositories/repository.go +++ b/api/pkg/repositories/repository.go @@ -1,15 +1,43 @@ package repositories -import "github.com/palantir/stacktrace" +import ( + "strings" + "time" + + "github.com/avast/retry-go/v5" + "github.com/palantir/stacktrace" +) // IndexParams parameters for indexing a database table type IndexParams struct { - Skip int `json:"skip"` - Query string `json:"query"` - Limit int `json:"take"` + Skip int `json:"skip"` + SortBy string `json:"sort"` + SortDescending bool `json:"sort_descending"` + Query string `json:"query"` + Limit int `json:"take"` } const ( // ErrCodeNotFound is thrown when an entity does not exist in storage ErrCodeNotFound = stacktrace.ErrorCode(1000) + + dbOperationDuration = 5 * time.Second ) + +// isRetryableError checks if the error is a retryable connection error +func isRetryableError(err error) bool { + msg := err.Error() + return strings.Contains(msg, "bad connection") || + strings.Contains(msg, "stream is closed") || + strings.Contains(msg, "driver: bad connection") +} + +// executeWithRetry executes a GORM query with retry logic for transient connection errors +func executeWithRetry(fn func() error) (err error) { + return retry.New( + retry.LastErrorOnly(true), + retry.Attempts(5), + retry.Delay(100*time.Millisecond), + retry.RetryIf(isRetryableError), + ).Do(fn) +} diff --git a/api/pkg/repositories/user_repository.go b/api/pkg/repositories/user_repository.go index 9bdb81ad..e0c59a36 100644 --- a/api/pkg/repositories/user_repository.go +++ b/api/pkg/repositories/user_repository.go @@ -14,15 +14,24 @@ type UserRepository interface { // Update a new entities.User Update(ctx context.Context, user *entities.User) error - // LoadAuthUser fetches an entities.AuthUser by apiKey - LoadAuthUser(ctx context.Context, apiKey string) (entities.AuthUser, error) + // LoadAuthContext fetches an entities.AuthContext by apiKey + LoadAuthContext(ctx context.Context, apiKey string) (entities.AuthContext, error) // Load an entities.User by entities.UserID Load(ctx context.Context, userID entities.UserID) (*entities.User, error) - // LoadOrStore an entities.User by entities.AuthUser - LoadOrStore(ctx context.Context, user entities.AuthUser) (*entities.User, bool, error) + // RotateAPIKey updates the API Key of a user + RotateAPIKey(ctx context.Context, userID entities.UserID) (*entities.User, error) + + // LoadOrStore an entities.User by entities.AuthContext + LoadOrStore(ctx context.Context, user entities.AuthContext) (*entities.User, bool, error) // LoadBySubscriptionID loads a user based on the lemonsqueezy subscriptionID LoadBySubscriptionID(ctx context.Context, subscriptionID string) (*entities.User, error) + + // LoadByEmail loads a user based on the email + LoadByEmail(ctx context.Context, email string) (*entities.User, error) + + // Delete an entities.User by entities.UserID + Delete(ctx context.Context, user *entities.User) error } diff --git a/api/pkg/repositories/webhook_repository.go b/api/pkg/repositories/webhook_repository.go index 361f4599..f1951fc0 100644 --- a/api/pkg/repositories/webhook_repository.go +++ b/api/pkg/repositories/webhook_repository.go @@ -24,4 +24,7 @@ type WebhookRepository interface { // Delete an entities.Webhook Delete(ctx context.Context, userID entities.UserID, webhookID uuid.UUID) error + + // DeleteAllForUser deletes all entities.Webhook for a user + DeleteAllForUser(ctx context.Context, userID entities.UserID) error } diff --git a/api/pkg/requests/bulk_message_request.go b/api/pkg/requests/bulk_message_request.go index ffb3f35c..77319997 100644 --- a/api/pkg/requests/bulk_message_request.go +++ b/api/pkg/requests/bulk_message_request.go @@ -18,6 +18,7 @@ type BulkMessage struct { ToPhoneNumber string `csv:"ToPhoneNumber"` Content string `csv:"Content"` SendTime *time.Time `csv:"SendTime(optional)"` + AttachmentURLs string `csv:"AttachmentURLs(optional)" validate:"optional"` // Comma separated list of URLs } // Sanitize sets defaults to BulkMessage @@ -25,12 +26,21 @@ func (input *BulkMessage) Sanitize() *BulkMessage { input.ToPhoneNumber = input.sanitizeAddress(input.ToPhoneNumber) input.Content = strings.TrimSpace(input.Content) input.FromPhoneNumber = input.sanitizeAddress(input.FromPhoneNumber) + + var attachments []string + for _, attachment := range strings.Split(input.AttachmentURLs, ",") { + if strings.TrimSpace(attachment) != "" { + attachments = append(attachments, strings.TrimSpace(attachment)) + } + } + input.AttachmentURLs = strings.Join(attachments, ",") return input } // ToMessageSendParams converts BulkMessage to services.MessageSendParams func (input *BulkMessage) ToMessageSendParams(userID entities.UserID, requestID uuid.UUID, source string) services.MessageSendParams { from, _ := phonenumbers.Parse(input.FromPhoneNumber, phonenumbers.UNKNOWN_REGION) + return services.MessageSendParams{ Source: source, Owner: from, @@ -40,5 +50,6 @@ func (input *BulkMessage) ToMessageSendParams(userID entities.UserID, requestID RequestReceivedAt: time.Now().UTC(), Contact: input.sanitizeAddress(input.ToPhoneNumber), Content: input.Content, + Attachments: input.removeEmptyStrings(strings.Split(input.AttachmentURLs, ",")), } } diff --git a/api/pkg/requests/discord_store_request.go b/api/pkg/requests/discord_store_request.go index f73458e1..dd4c91a0 100644 --- a/api/pkg/requests/discord_store_request.go +++ b/api/pkg/requests/discord_store_request.go @@ -24,7 +24,7 @@ func (input *DiscordStore) Sanitize() DiscordStore { } // ToStoreParams converts DiscordStore to services.WebhookStoreParams -func (input *DiscordStore) ToStoreParams(user entities.AuthUser) *services.DiscordStoreParams { +func (input *DiscordStore) ToStoreParams(user entities.AuthContext) *services.DiscordStoreParams { return &services.DiscordStoreParams{ UserID: user.ID, Name: input.Name, diff --git a/api/pkg/requests/discord_update_request.go b/api/pkg/requests/discord_update_request.go index 30b05d95..3b30e49d 100644 --- a/api/pkg/requests/discord_update_request.go +++ b/api/pkg/requests/discord_update_request.go @@ -19,7 +19,7 @@ func (input *DiscordUpdate) Sanitize() DiscordUpdate { } // ToUpdateParams converts DiscordUpdate to services.DiscordUpdateParams -func (input *DiscordUpdate) ToUpdateParams(user entities.AuthUser) *services.DiscordUpdateParams { +func (input *DiscordUpdate) ToUpdateParams(user entities.AuthContext) *services.DiscordUpdateParams { return &services.DiscordUpdateParams{ UserID: user.ID, Name: input.Name, diff --git a/api/pkg/requests/heartbeat_store_request.go b/api/pkg/requests/heartbeat_store_request.go index 99e170f9..996225b9 100644 --- a/api/pkg/requests/heartbeat_store_request.go +++ b/api/pkg/requests/heartbeat_store_request.go @@ -10,23 +10,35 @@ import ( // HeartbeatStore is the payload for fetching entities.Heartbeat of a phone number type HeartbeatStore struct { request - Charging bool `json:"charging"` - Owner string `json:"owner"` + Owner string `json:"owner" swaggerignore:"true"` + Charging bool `json:"charging"` + PhoneNumbers []string `json:"phone_numbers"` } // Sanitize sets defaults to MessageOutstanding func (input *HeartbeatStore) Sanitize() HeartbeatStore { input.Owner = input.sanitizeAddress(input.Owner) + + input.PhoneNumbers = input.sanitizeAddresses(input.PhoneNumbers) + if len(input.PhoneNumbers) == 0 { + input.PhoneNumbers = append(input.PhoneNumbers, input.Owner) + } + return *input } // ToStoreParams converts HeartbeatIndex to repositories.IndexParams -func (input *HeartbeatStore) ToStoreParams(user entities.AuthUser, version string) services.HeartbeatStoreParams { - return services.HeartbeatStoreParams{ - Owner: input.Owner, - Version: version, - Charging: input.Charging, - Timestamp: time.Now().UTC(), - UserID: user.ID, +func (input *HeartbeatStore) ToStoreParams(user entities.AuthContext, source string, version string) []services.HeartbeatStoreParams { + var params []services.HeartbeatStoreParams + for _, phoneNumber := range input.PhoneNumbers { + params = append(params, services.HeartbeatStoreParams{ + Owner: phoneNumber, + Charging: input.Charging, + Source: source, + Version: version, + UserID: user.ID, + Timestamp: time.Now(), + }) } + return params } diff --git a/api/pkg/requests/message_bulk_send_request.go b/api/pkg/requests/message_bulk_send_request.go index 07c8dc27..461ac2ed 100644 --- a/api/pkg/requests/message_bulk_send_request.go +++ b/api/pkg/requests/message_bulk_send_request.go @@ -1,6 +1,7 @@ package requests import ( + "strings" "time" "github.com/NdoleStudio/httpsms/pkg/entities" @@ -17,6 +18,12 @@ type MessageBulkSend struct { To []string `json:"to" example:"+18005550100,+18005550100"` Content string `json:"content" example:"This is a sample text message"` + // Attachments are optional. When you provide a list of attachments, the message will be sent out as an MMS + Attachments []string `json:"attachments" validate:"optional"` + + // Encrypted is used to determine if the content is end-to-end encrypted. Make sure to set the encryption key on the httpSMS mobile app + Encrypted bool `json:"encrypted" example:"false" validate:"optional"` + // RequestID is an optional parameter used to track a request from the client's perspective RequestID string `json:"request_id" example:"153554b5-ae44-44a0-8f4f-7bbac5657ad4" validate:"optional"` } @@ -27,6 +34,15 @@ func (input *MessageBulkSend) Sanitize() MessageBulkSend { for _, address := range input.To { to = append(to, input.sanitizeAddress(address)) } + + var attachments []string + for _, attachment := range input.Attachments { + if strings.TrimSpace(attachment) != "" { + attachments = append(attachments, strings.TrimSpace(attachment)) + } + } + + input.Attachments = attachments input.To = to input.From = input.sanitizeAddress(input.From) return *input @@ -37,15 +53,19 @@ func (input *MessageBulkSend) ToMessageSendParams(userID entities.UserID, source from, _ := phonenumbers.Parse(input.From, phonenumbers.UNKNOWN_REGION) var result []services.MessageSendParams - for _, to := range input.To { + for index, to := range input.To { + sendAt := time.Now().UTC().Add(time.Duration(index) * time.Second) result = append(result, services.MessageSendParams{ Source: source, Owner: from, + Encrypted: input.Encrypted, RequestID: input.sanitizeStringPointer(input.RequestID), UserID: userID, RequestReceivedAt: time.Now().UTC(), Contact: to, + SendAt: &sendAt, Content: input.Content, + Attachments: input.Attachments, }) } diff --git a/api/pkg/requests/message_call_missed_request.go b/api/pkg/requests/message_call_missed_request.go new file mode 100644 index 00000000..1f880775 --- /dev/null +++ b/api/pkg/requests/message_call_missed_request.go @@ -0,0 +1,42 @@ +package requests + +import ( + "time" + + "github.com/NdoleStudio/httpsms/pkg/entities" + + "github.com/nyaruka/phonenumbers" + + "github.com/NdoleStudio/httpsms/pkg/services" +) + +// MessageCallMissed is the payload for sending and missed call event +type MessageCallMissed struct { + request + From string `json:"from" example:"+18005550199"` + To string `json:"to" example:"+18005550100"` + SIM string `json:"sim" example:"SIM1"` + Timestamp time.Time `json:"timestamp" example:"2022-06-05T14:26:09.527976+03:00"` +} + +// Sanitize sets defaults to MessageReceive +func (input *MessageCallMissed) Sanitize() MessageCallMissed { + input.To = input.sanitizeAddress(input.To) + input.From = input.sanitizeContact(input.To, input.From) + input.SIM = input.sanitizeSIM(input.SIM) + + return *input +} + +// ToCallMissedParams converts MessageCallMissed to services.MessageSendParams +func (input *MessageCallMissed) ToCallMissedParams(userID entities.UserID, source string) *services.MissedCallParams { + to, _ := phonenumbers.Parse(input.To, phonenumbers.UNKNOWN_REGION) + return &services.MissedCallParams{ + Source: source, + Owner: to, + Timestamp: input.Timestamp, + SIM: entities.SIM(input.SIM), + UserID: userID, + Contact: input.From, + } +} diff --git a/api/pkg/requests/message_outstanding_request.go b/api/pkg/requests/message_outstanding_request.go index a1a768eb..3d3209e9 100644 --- a/api/pkg/requests/message_outstanding_request.go +++ b/api/pkg/requests/message_outstanding_request.go @@ -22,11 +22,12 @@ func (input *MessageOutstanding) Sanitize() MessageOutstanding { } // ToGetOutstandingParams converts MessageOutstanding into services.MessageGetOutstandingParams -func (input *MessageOutstanding) ToGetOutstandingParams(source string, userID entities.UserID, timestamp time.Time) services.MessageGetOutstandingParams { +func (input *MessageOutstanding) ToGetOutstandingParams(source string, authCtx entities.AuthContext, timestamp time.Time) services.MessageGetOutstandingParams { return services.MessageGetOutstandingParams{ - Source: source, - UserID: userID, - MessageID: uuid.MustParse(input.MessageID), - Timestamp: timestamp, + Source: source, + PhoneNumbers: authCtx.PhoneNumbers, + UserID: authCtx.ID, + MessageID: uuid.MustParse(input.MessageID), + Timestamp: timestamp, } } diff --git a/api/pkg/requests/message_receive_request.go b/api/pkg/requests/message_receive_request.go index 158d9cbe..b89cddfa 100644 --- a/api/pkg/requests/message_receive_request.go +++ b/api/pkg/requests/message_receive_request.go @@ -11,22 +11,36 @@ import ( "github.com/NdoleStudio/httpsms/pkg/services" ) +// MessageAttachment represents a single MMS attachment in a receive request +type MessageAttachment struct { + // Name is the original filename of the attachment + Name string `json:"name" example:"photo.jpg"` + // ContentType is the MIME type of the attachment + ContentType string `json:"content_type" example:"image/jpeg"` + // Content is the base64-encoded attachment data + Content string `json:"content" example:"base64data..."` +} + // MessageReceive is the payload for sending and SMS message type MessageReceive struct { request From string `json:"from" example:"+18005550199"` To string `json:"to" example:"+18005550100"` Content string `json:"content" example:"This is a sample text message received on a phone"` + // Encrypted is used to determine if the content is end-to-end encrypted. Make sure to set the encryption key on the httpSMS mobile app + Encrypted bool `json:"encrypted" example:"false"` // SIM card that received the message SIM entities.SIM `json:"sim" example:"SIM1"` // Timestamp is the time when the event was emitted, Please send the timestamp in UTC with as much precision as possible Timestamp time.Time `json:"timestamp" example:"2022-06-05T14:26:09.527976+03:00"` + // Attachments is the list of MMS attachments received with the message + Attachments []MessageAttachment `json:"attachments" validate:"optional"` } // Sanitize sets defaults to MessageReceive func (input *MessageReceive) Sanitize() MessageReceive { input.To = input.sanitizeAddress(input.To) - input.From = input.sanitizeAddress(input.From) + input.From = input.sanitizeContact(input.To, input.From) if strings.TrimSpace(string(input.SIM)) == "" || input.SIM == ("DEFAULT") { input.SIM = entities.SIM1 } @@ -34,15 +48,27 @@ func (input *MessageReceive) Sanitize() MessageReceive { } // ToMessageReceiveParams converts MessageReceive to services.MessageReceiveParams -func (input *MessageReceive) ToMessageReceiveParams(userID entities.UserID, source string) services.MessageReceiveParams { +func (input *MessageReceive) ToMessageReceiveParams(userID entities.UserID, source string) *services.MessageReceiveParams { phone, _ := phonenumbers.Parse(input.To, phonenumbers.UNKNOWN_REGION) - return services.MessageReceiveParams{ - Source: source, - Contact: input.From, - UserID: userID, - Timestamp: input.Timestamp, - Owner: *phone, - Content: input.Content, - SIM: input.SIM, + + attachments := make([]services.ServiceAttachment, len(input.Attachments)) + for i, a := range input.Attachments { + attachments[i] = services.ServiceAttachment{ + Name: a.Name, + ContentType: a.ContentType, + Content: a.Content, + } + } + + return &services.MessageReceiveParams{ + Source: source, + Contact: input.From, + UserID: userID, + Timestamp: input.Timestamp, + Encrypted: input.Encrypted, + Owner: *phone, + Content: input.Content, + SIM: input.SIM, + Attachments: attachments, } } diff --git a/api/pkg/requests/message_search_request.go b/api/pkg/requests/message_search_request.go new file mode 100644 index 00000000..e181115f --- /dev/null +++ b/api/pkg/requests/message_search_request.go @@ -0,0 +1,70 @@ +package requests + +import ( + "strings" + + "github.com/NdoleStudio/httpsms/pkg/entities" + + "github.com/NdoleStudio/httpsms/pkg/repositories" + + "github.com/NdoleStudio/httpsms/pkg/services" +) + +// MessageSearch is the payload fetching entities.Message +type MessageSearch struct { + request + Skip string `json:"skip" query:"skip"` + Owners []string `json:"owners" query:"owners"` + Types []string `json:"types" query:"types"` + Statuses []string `json:"statuses" query:"statuses"` + Query string `json:"query" query:"query"` + SortBy string `json:"sort_by" query:"sort_by"` + SortDescending bool `json:"sort_descending" query:"sort_descending"` + Limit string `json:"limit" query:"limit"` + + IPAddress string `json:"ip_address" swaggerignore:"true"` + Token string `json:"token" swaggerignore:"true"` +} + +// Sanitize sets defaults to MessageSearch +func (input *MessageSearch) Sanitize() MessageSearch { + if strings.TrimSpace(input.Limit) == "" { + input.Limit = "100" + } + + input.Query = strings.TrimSpace(input.Query) + + input.Skip = strings.TrimSpace(input.Skip) + if input.Skip == "" { + input.Skip = "0" + } + + return *input +} + +// ToSearchParams converts request to services.MessageSearchParams +func (input *MessageSearch) ToSearchParams(userID entities.UserID) *services.MessageSearchParams { + var types []entities.MessageType + for _, t := range input.Types { + types = append(types, entities.MessageType(t)) + } + + var statuses []entities.MessageStatus + for _, s := range input.Statuses { + statuses = append(statuses, entities.MessageStatus(s)) + } + + return &services.MessageSearchParams{ + IndexParams: repositories.IndexParams{ + Skip: input.getInt(input.Skip), + Query: input.Query, + SortBy: input.SortBy, + SortDescending: input.SortDescending, + Limit: input.getInt(input.Limit), + }, + UserID: userID, + Owners: input.Owners, + Types: types, + Statuses: statuses, + } +} diff --git a/api/pkg/requests/message_send_request.go b/api/pkg/requests/message_send_request.go index 6c632e92..727cc12e 100644 --- a/api/pkg/requests/message_send_request.go +++ b/api/pkg/requests/message_send_request.go @@ -18,10 +18,15 @@ type MessageSend struct { To string `json:"to" example:"+18005550100"` Content string `json:"content" example:"This is a sample text message"` + // Attachments are optional. When you provide a list of attachments, the message will be sent out as an MMS + Attachments []string `json:"attachments" validate:"optional" example:"https://example.com/image.jpg,https://example.com/video.mp4"` + + // Encrypted is an optional parameter used to determine if the content is end-to-end encrypted. Make sure to set the encryption key on the httpSMS mobile app + Encrypted bool `json:"encrypted" example:"false" validate:"optional"` // RequestID is an optional parameter used to track a request from the client's perspective RequestID string `json:"request_id" example:"153554b5-ae44-44a0-8f4f-7bbac5657ad4" validate:"optional"` - // SendAt is an optional parameter used to schedule a message to be sent at a later time - SendAt *time.Time `json:"send_at" example:"2022-06-05T14:26:09.527976+03:00" validate:"optional"` + // SendAt is an optional parameter used to schedule a message to be sent in the future. The time is considered to be in your profile's local timezone and you can queue messages for up to 20 days (480 hours) in the future. + SendAt *time.Time `json:"send_at" example:"2025-12-19T16:39:57-08:00" validate:"optional"` } // Sanitize sets defaults to MessageReceive @@ -29,6 +34,13 @@ func (input *MessageSend) Sanitize() MessageSend { input.To = input.sanitizeAddress(input.To) input.RequestID = strings.TrimSpace(input.RequestID) input.From = input.sanitizeAddress(input.From) + var attachments []string + for _, attachment := range input.Attachments { + if strings.TrimSpace(attachment) != "" { + attachments = append(attachments, strings.TrimSpace(attachment)) + } + } + input.Attachments = attachments return *input } @@ -38,11 +50,13 @@ func (input *MessageSend) ToMessageSendParams(userID entities.UserID, source str return services.MessageSendParams{ Source: source, Owner: from, + Encrypted: input.Encrypted, RequestID: input.sanitizeStringPointer(input.RequestID), UserID: userID, SendAt: input.SendAt, RequestReceivedAt: time.Now().UTC(), Contact: input.sanitizeAddress(input.To), Content: input.Content, + Attachments: input.Attachments, } } diff --git a/api/pkg/requests/phone_api_key_index_request.go b/api/pkg/requests/phone_api_key_index_request.go new file mode 100644 index 00000000..49649f28 --- /dev/null +++ b/api/pkg/requests/phone_api_key_index_request.go @@ -0,0 +1,37 @@ +package requests + +import ( + "strings" + + "github.com/NdoleStudio/httpsms/pkg/repositories" +) + +// PhoneAPIKeyIndex is the payload for fetching entities.PhoneAPIKey of a user +type PhoneAPIKeyIndex struct { + request + Skip string `json:"skip" query:"skip"` + Query string `json:"query" query:"query"` + Limit string `json:"limit" query:"limit"` +} + +// Sanitize sets defaults to MessageOutstanding +func (input *PhoneAPIKeyIndex) Sanitize() PhoneAPIKeyIndex { + if strings.TrimSpace(input.Limit) == "" { + input.Limit = "1" + } + input.Query = strings.TrimSpace(input.Query) + input.Skip = strings.TrimSpace(input.Skip) + if input.Skip == "" { + input.Skip = "0" + } + return *input +} + +// ToIndexParams converts HeartbeatIndex to repositories.IndexParams +func (input *PhoneAPIKeyIndex) ToIndexParams() repositories.IndexParams { + return repositories.IndexParams{ + Skip: input.getInt(input.Skip), + Query: input.Query, + Limit: input.getInt(input.Limit), + } +} diff --git a/api/pkg/requests/phone_api_key_store_request.go b/api/pkg/requests/phone_api_key_store_request.go new file mode 100644 index 00000000..e7df2484 --- /dev/null +++ b/api/pkg/requests/phone_api_key_store_request.go @@ -0,0 +1,13 @@ +package requests + +// PhoneAPIKeyStoreRequest is the payload for storing a phone API key +type PhoneAPIKeyStoreRequest struct { + request + Name string `json:"name" example:"My Phone API Key"` +} + +// Sanitize sets defaults to MessageReceive +func (input *PhoneAPIKeyStoreRequest) Sanitize() PhoneAPIKeyStoreRequest { + input.Name = input.sanitizeAddress(input.Name) + return *input +} diff --git a/api/pkg/requests/phone_fcm_token_request.go b/api/pkg/requests/phone_fcm_token_request.go new file mode 100644 index 00000000..dde935b5 --- /dev/null +++ b/api/pkg/requests/phone_fcm_token_request.go @@ -0,0 +1,40 @@ +package requests + +import ( + "strings" + + "github.com/nyaruka/phonenumbers" + + "github.com/NdoleStudio/httpsms/pkg/entities" + "github.com/NdoleStudio/httpsms/pkg/services" +) + +// PhoneFCMToken is the payload for updating the FCM token of a phone +type PhoneFCMToken struct { + request + PhoneNumber string `json:"phone_number" example:"[+18005550199]"` + FcmToken string `json:"fcm_token" example:"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzd....."` + // SIM is the SIM slot of the phone in case the phone has more than 1 SIM slot + SIM string `json:"sim" example:"SIM1"` +} + +// Sanitize sets defaults to MessageOutstanding +func (input *PhoneFCMToken) Sanitize() PhoneFCMToken { + input.FcmToken = strings.TrimSpace(input.FcmToken) + input.PhoneNumber = input.sanitizeAddress(input.PhoneNumber) + input.SIM = input.sanitizeSIM(input.SIM) + return *input +} + +// ToPhoneFCMTokenParams converts PhoneFCMToken to services.PhoneFCMTokenParams +func (input *PhoneFCMToken) ToPhoneFCMTokenParams(user entities.AuthContext, source string) *services.PhoneFCMTokenParams { + phone, _ := phonenumbers.Parse(input.PhoneNumber, phonenumbers.UNKNOWN_REGION) + return &services.PhoneFCMTokenParams{ + Source: source, + PhoneNumber: phone, + PhoneAPIKeyID: user.PhoneAPIKeyID, + UserID: user.ID, + FcmToken: &input.FcmToken, + SIM: entities.SIM(input.SIM), + } +} diff --git a/api/pkg/requests/phone_index_request.go b/api/pkg/requests/phone_index_request.go index f978bb52..210efaf4 100644 --- a/api/pkg/requests/phone_index_request.go +++ b/api/pkg/requests/phone_index_request.go @@ -17,7 +17,7 @@ type PhoneIndex struct { // Sanitize sets defaults to MessageOutstanding func (input *PhoneIndex) Sanitize() PhoneIndex { if strings.TrimSpace(input.Limit) == "" { - input.Limit = "1" + input.Limit = "10" } input.Query = strings.TrimSpace(input.Query) input.Skip = strings.TrimSpace(input.Skip) diff --git a/api/pkg/requests/phone_update_request.go b/api/pkg/requests/phone_update_request.go index 956eb867..f920fad4 100644 --- a/api/pkg/requests/phone_update_request.go +++ b/api/pkg/requests/phone_update_request.go @@ -24,6 +24,8 @@ type PhoneUpsert struct { FcmToken string `json:"fcm_token" example:"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzd....."` + MissedCallAutoReply *string `json:"missed_call_auto_reply" example:"e.g. This phone cannot receive calls. Please send an SMS instead."` + // SIM is the SIM slot of the phone in case the phone has more than 1 SIM slot SIM string `json:"sim" example:"SIM1"` } @@ -32,14 +34,15 @@ type PhoneUpsert struct { func (input *PhoneUpsert) Sanitize() PhoneUpsert { input.FcmToken = strings.TrimSpace(input.FcmToken) input.PhoneNumber = input.sanitizeAddress(input.PhoneNumber) - if input.SIM == "" { - input.SIM = entities.SIM1.String() + input.SIM = input.sanitizeSIM(input.SIM) + if input.MissedCallAutoReply != nil { + input.MissedCallAutoReply = input.sanitizeStringPointer(*input.MissedCallAutoReply) } return *input } // ToUpsertParams converts PhoneUpsert to services.PhoneUpsertParams -func (input *PhoneUpsert) ToUpsertParams(user entities.AuthUser, source string) services.PhoneUpsertParams { +func (input *PhoneUpsert) ToUpsertParams(user entities.AuthContext, source string) *services.PhoneUpsertParams { phone, _ := phonenumbers.Parse(input.PhoneNumber, phonenumbers.UNKNOWN_REGION) // ignore value if it's default @@ -66,10 +69,11 @@ func (input *PhoneUpsert) ToUpsertParams(user entities.AuthUser, source string) maxSendAttempts = &input.MaxSendAttempts } - return services.PhoneUpsertParams{ + return &services.PhoneUpsertParams{ Source: source, - PhoneNumber: *phone, + PhoneNumber: phone, MessagesPerMinute: messagesPerMinute, + MissedCallAutoReply: input.MissedCallAutoReply, MessageExpirationDuration: timeout, MaxSendAttempts: maxSendAttempts, FcmToken: fcmToken, diff --git a/api/pkg/requests/request.go b/api/pkg/requests/request.go index 8b244cc3..1db27861 100644 --- a/api/pkg/requests/request.go +++ b/api/pkg/requests/request.go @@ -6,18 +6,23 @@ import ( "strings" "unicode" + "github.com/NdoleStudio/httpsms/pkg/entities" + "github.com/nyaruka/phonenumbers" ) type request struct{} -// getLimit gets the take as a string -func (input *request) sanitizeAddress(value string) string { - value = strings.TrimRight(value, " ") - if len(value) > 0 && value[0] == ' ' { - value = strings.Replace(value, " ", "+", 1) +func (input *request) sanitizeAddresses(value []string) []string { + var result []string + for _, address := range value { + result = append(result, input.sanitizeAddress(address)) } + return result +} +func (input *request) sanitizeAddress(value string) string { + value = strings.TrimSpace(value) if !strings.HasPrefix(value, "+") && input.isDigits(value) && len(value) > 9 { value = "+" + value } @@ -29,6 +34,25 @@ func (input *request) sanitizeAddress(value string) string { return value } +func (input *request) sanitizeContact(owner string, contact string) string { + contact = strings.TrimSpace(contact) + + if len(contact) < 8 || !input.isDigits(contact) { + return contact + } + + regionPhoneNumber, err := phonenumbers.Parse(owner, phonenumbers.UNKNOWN_REGION) + if err != nil { + return contact + } + + if number, err := phonenumbers.Parse(contact, phonenumbers.GetRegionCodeForNumber(regionPhoneNumber)); err == nil { + contact = phonenumbers.Format(number, phonenumbers.E164) + } + + return contact +} + // sanitizeBool sanitizes a boolean string func (input *request) sanitizeBool(value string) string { value = strings.TrimSpace(value) @@ -43,6 +67,13 @@ func (input *request) sanitizeBool(value string) string { return value } +func (input *request) sanitizeSIM(value string) string { + if value == entities.SIM1.String() || value == entities.SIM2.String() { + return value + } + return entities.SIM1.String() +} + func (input *request) sanitizeURL(value string) string { value = strings.TrimSpace(value) website, err := url.Parse(value) @@ -77,6 +108,18 @@ func (input *request) removeStringDuplicates(values []string) []string { return result } +func (input *request) removeEmptyStrings(values []string) []string { + var result []string + for _, value := range values { + value = strings.TrimSpace(value) + if value != "" { + result = append(result, value) + } + } + + return result +} + func (input *request) sanitizeMessageID(value string) string { id := strings.Builder{} for _, char := range value { diff --git a/api/pkg/requests/user_notification_update_request.go b/api/pkg/requests/user_notification_update_request.go index e84e57a8..b8b92a54 100644 --- a/api/pkg/requests/user_notification_update_request.go +++ b/api/pkg/requests/user_notification_update_request.go @@ -10,6 +10,7 @@ type UserNotificationUpdate struct { MessageStatusEnabled bool `json:"message_status_enabled" example:"true"` WebhookEnabled bool `json:"webhook_enabled" example:"true"` HeartbeatEnabled bool `json:"heartbeat_enabled" example:"true"` + NewsletterEnabled bool `json:"newsletter_enabled" example:"true"` } // ToUserNotificationUpdateParams converts UserNotificationUpdate to services.UserNotificationUpdateParams @@ -18,5 +19,6 @@ func (input *UserNotificationUpdate) ToUserNotificationUpdateParams() *services. MessageStatusEnabled: input.MessageStatusEnabled, WebhookEnabled: input.WebhookEnabled, HeartbeatEnabled: input.HeartbeatEnabled, + NewsletterEnabled: input.NewsletterEnabled, } } diff --git a/api/pkg/requests/user_payment_invoice_request.go b/api/pkg/requests/user_payment_invoice_request.go new file mode 100644 index 00000000..4d196f4a --- /dev/null +++ b/api/pkg/requests/user_payment_invoice_request.go @@ -0,0 +1,46 @@ +package requests + +import ( + "github.com/NdoleStudio/httpsms/pkg/entities" + "github.com/NdoleStudio/httpsms/pkg/services" +) + +// UserPaymentInvoice is the payload for generating a subscription payment invoice +type UserPaymentInvoice struct { + request + Name string `json:"name" example:"Acme Corp"` + Address string `json:"address" example:"221B Baker Street, London"` + City string `json:"city" example:"Los Angeles"` + State string `json:"state" example:"CA"` + Country string `json:"country" example:"US"` + ZipCode string `json:"zip_code" example:"9800"` + Notes string `json:"notes" example:"Thank you for your business!"` + SubscriptionInvoiceID string `json:"subscriptionInvoiceID" swaggerignore:"true"` // used internally for validation +} + +// Sanitize sets defaults to MessageReceive +func (input *UserPaymentInvoice) Sanitize() UserPaymentInvoice { + input.Name = input.sanitizeAddress(input.Name) + input.Address = input.sanitizeAddress(input.Address) + input.City = input.sanitizeAddress(input.City) + input.State = input.sanitizeAddress(input.State) + input.Country = input.sanitizeAddress(input.Country) + input.ZipCode = input.sanitizeAddress(input.ZipCode) + input.Notes = input.sanitizeAddress(input.Notes) + return *input +} + +// UserInvoiceGenerateParams converts UserPaymentInvoice to services.UserInvoiceGenerateParams +func (input *UserPaymentInvoice) UserInvoiceGenerateParams(userID entities.UserID) *services.UserInvoiceGenerateParams { + return &services.UserInvoiceGenerateParams{ + UserID: userID, + SubscriptionInvoiceID: input.SubscriptionInvoiceID, + Name: input.Name, + Address: input.Address, + City: input.City, + State: input.State, + Country: input.Country, + Notes: input.Notes, + ZipCode: input.ZipCode, + } +} diff --git a/api/pkg/requests/user_update_request.go b/api/pkg/requests/user_update_request.go index 678c7824..d6848f47 100644 --- a/api/pkg/requests/user_update_request.go +++ b/api/pkg/requests/user_update_request.go @@ -29,8 +29,15 @@ func (input *UserUpdate) ToUpdateParams() services.UserUpdateParams { if err != nil { location = time.UTC } + + var activePhoneID *uuid.UUID + if input.ActivePhoneID != "" { + val := uuid.MustParse(input.ActivePhoneID) + activePhoneID = &val + } + return services.UserUpdateParams{ - ActivePhoneID: uuid.MustParse(input.ActivePhoneID), + ActivePhoneID: activePhoneID, Timezone: location, } } diff --git a/api/pkg/requests/webhook_store_request.go b/api/pkg/requests/webhook_store_request.go index fca0a7d2..f45ee170 100644 --- a/api/pkg/requests/webhook_store_request.go +++ b/api/pkg/requests/webhook_store_request.go @@ -31,7 +31,7 @@ func (input *WebhookStore) Sanitize() WebhookStore { } // ToStoreParams converts WebhookStore to services.WebhookStoreParams -func (input *WebhookStore) ToStoreParams(user entities.AuthUser) *services.WebhookStoreParams { +func (input *WebhookStore) ToStoreParams(user entities.AuthContext) *services.WebhookStoreParams { return &services.WebhookStoreParams{ UserID: user.ID, SigningKey: input.SigningKey, diff --git a/api/pkg/requests/webhook_update_request.go b/api/pkg/requests/webhook_update_request.go index 30eb95dc..aeaf989e 100644 --- a/api/pkg/requests/webhook_update_request.go +++ b/api/pkg/requests/webhook_update_request.go @@ -19,7 +19,7 @@ func (input *WebhookUpdate) Sanitize() WebhookUpdate { } // ToUpdateParams converts WebhookUpdate to services.WebhookUpdateParams -func (input *WebhookUpdate) ToUpdateParams(user entities.AuthUser) *services.WebhookUpdateParams { +func (input *WebhookUpdate) ToUpdateParams(user entities.AuthContext) *services.WebhookUpdateParams { return &services.WebhookUpdateParams{ UserID: user.ID, WebhookID: uuid.MustParse(input.WebhookID), diff --git a/api/pkg/responses/billing_responses.go b/api/pkg/responses/billing_responses.go index bb51d6ab..0ce46415 100644 --- a/api/pkg/responses/billing_responses.go +++ b/api/pkg/responses/billing_responses.go @@ -1,6 +1,8 @@ package responses -import "github.com/NdoleStudio/httpsms/pkg/entities" +import ( + "github.com/NdoleStudio/httpsms/pkg/entities" +) // BillingUsagesResponse is the payload containing []entities.BillingUsage type BillingUsagesResponse struct { diff --git a/api/pkg/responses/phone_api_key_responses.go b/api/pkg/responses/phone_api_key_responses.go new file mode 100644 index 00000000..fff077c2 --- /dev/null +++ b/api/pkg/responses/phone_api_key_responses.go @@ -0,0 +1,15 @@ +package responses + +import "github.com/NdoleStudio/httpsms/pkg/entities" + +// PhoneAPIKeyResponse is the payload containing an entities.PhoneAPIKey +type PhoneAPIKeyResponse struct { + response + Data *entities.PhoneAPIKey `json:"data"` +} + +// PhoneAPIKeysResponse is the payload containing []entities.PhoneAPIKey +type PhoneAPIKeysResponse struct { + response + Data []*entities.PhoneAPIKey `json:"data"` +} diff --git a/api/pkg/responses/response.go b/api/pkg/responses/response.go index 5b98866f..c61c919e 100644 --- a/api/pkg/responses/response.go +++ b/api/pkg/responses/response.go @@ -2,7 +2,7 @@ package responses type response struct { Status string `json:"status" example:"success"` - Message string `json:"message" example:"item created successfully"` + Message string `json:"message" example:"Request handled successfully"` } // InternalServerError is the response with status code is 500 @@ -27,7 +27,7 @@ type BadRequest struct { // UnprocessableEntity is the response with status code is 422 type UnprocessableEntity struct { Status string `json:"status" example:"error"` - Message string `json:"message" example:"validation errors while sending message"` + Message string `json:"message" example:"validation errors while handling request"` Data map[string][]string `json:"data"` } diff --git a/api/pkg/responses/user_responses.go b/api/pkg/responses/user_responses.go index f2ee6c37..31f95341 100644 --- a/api/pkg/responses/user_responses.go +++ b/api/pkg/responses/user_responses.go @@ -1,9 +1,51 @@ package responses -import "github.com/NdoleStudio/httpsms/pkg/entities" +import ( + "time" + + "github.com/NdoleStudio/httpsms/pkg/entities" +) // UserResponse is the payload containing entities.User type UserResponse struct { response Data entities.User `json:"data"` } + +// UserSubscriptionPaymentsResponse is the payload containing lemonsqueezy.SubscriptionInvoicesAPIResponse +type UserSubscriptionPaymentsResponse struct { + response + Data []struct { + Type string `json:"type"` + ID string `json:"id"` + Attributes struct { + BillingReason string `json:"billing_reason"` + CardBrand string `json:"card_brand"` + CardLastFour string `json:"card_last_four"` + Currency string `json:"currency"` + CurrencyRate string `json:"currency_rate"` + Status string `json:"status"` + StatusFormatted string `json:"status_formatted"` + Refunded bool `json:"refunded"` + RefundedAt any `json:"refunded_at"` + Subtotal int `json:"subtotal"` + DiscountTotal int `json:"discount_total"` + Tax int `json:"tax"` + TaxInclusive bool `json:"tax_inclusive"` + Total int `json:"total"` + RefundedAmount int `json:"refunded_amount"` + SubtotalUsd int `json:"subtotal_usd"` + DiscountTotalUsd int `json:"discount_total_usd"` + TaxUsd int `json:"tax_usd"` + TotalUsd int `json:"total_usd"` + RefundedAmountUsd int `json:"refunded_amount_usd"` + SubtotalFormatted string `json:"subtotal_formatted"` + DiscountTotalFormatted string `json:"discount_total_formatted"` + TaxFormatted string `json:"tax_formatted"` + TotalFormatted string `json:"total_formatted"` + RefundedAmountFormatted string `json:"refunded_amount_formatted"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + } `json:"attributes"` + } `json:"data"` +} diff --git a/api/pkg/services/billing_service.go b/api/pkg/services/billing_service.go index c9a19974..1d573dbd 100644 --- a/api/pkg/services/billing_service.go +++ b/api/pkg/services/billing_service.go @@ -170,6 +170,20 @@ func (service *BillingService) RegisterReceivedMessage(ctx context.Context, mess return nil } +// DeleteAllForUser deletes all entities.BillingUsage for an entities.UserID. +func (service *BillingService) DeleteAllForUser(ctx context.Context, userID entities.UserID) error { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + if err := service.billingUsageRepository.DeleteAllForUser(ctx, userID); err != nil { + msg := fmt.Sprintf("could not delete [entities.BillingUsage] for user with ID [%s]", userID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("deleted all [entities.BillingUsage] for user with ID [%s]", userID)) + return nil +} + func (service *BillingService) sendUsageAlert(ctx context.Context, userID entities.UserID) { ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) defer span.End() @@ -207,7 +221,7 @@ func (service *BillingService) sendUsageAlert(ctx context.Context, userID entiti } func (service *BillingService) shouldSendAlert(user *entities.User, usage *entities.BillingUsage) bool { - if !user.IsOnProPlan() && (usage.TotalMessages() == 160 || usage.TotalMessages() == 180 || usage.TotalMessages() == 190) { + if user.IsOnFreePlan() && (usage.TotalMessages() == 160 || usage.TotalMessages() == 180 || usage.TotalMessages() == 190) { return true } @@ -219,5 +233,9 @@ func (service *BillingService) shouldSendAlert(user *entities.User, usage *entit return true } + if user.IsOn20kPlan() && (usage.TotalMessages() == 16000 || usage.TotalMessages() == 18000 || usage.TotalMessages() == 19000) { + return true + } + return false } diff --git a/api/pkg/services/discord_service.go b/api/pkg/services/discord_service.go index 3bb14d58..8c608e9f 100644 --- a/api/pkg/services/discord_service.go +++ b/api/pkg/services/discord_service.go @@ -53,6 +53,20 @@ func (service *DiscordService) GetByServerID(ctx context.Context, serverID strin return service.repository.FindByServerID(ctx, serverID) } +// DeleteAllForUser deletes all entities.Discord for an entities.UserID. +func (service *DiscordService) DeleteAllForUser(ctx context.Context, userID entities.UserID) error { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + if err := service.repository.DeleteAllForUser(ctx, userID); err != nil { + msg := fmt.Sprintf("could not delete all [entities.Discord] for user with ID [%s]", userID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("deleted all [entities.Discord] for user with ID [%s]", userID)) + return nil +} + // Index fetches the entities.Discord for an entities.UserID func (service *DiscordService) Index(ctx context.Context, userID entities.UserID, params repositories.IndexParams) ([]*entities.Discord, error) { ctx, span := service.tracer.Start(ctx) @@ -155,6 +169,12 @@ func (service *DiscordService) createSlashCommand(ctx context.Context, serverID Type: 3, Required: true, }, + { + Name: "attachment_urls", + Description: "Comma-separated list of media URLs to attach", + Type: 3, + Required: false, + }, }, }) if err != nil { diff --git a/api/pkg/services/email_notification_service.go b/api/pkg/services/email_notification_service.go index 1fa9bc78..20f45152 100644 --- a/api/pkg/services/email_notification_service.go +++ b/api/pkg/services/email_notification_service.go @@ -75,7 +75,7 @@ func (service *EmailNotificationService) NotifyMessageExpired(ctx context.Contex return nil } - email, err := service.factory.MessageExpired(user, payload.MessageID, payload.Owner, payload.Contact, payload.Content) + email, err := service.factory.MessageExpired(user, payload) if err != nil { msg := fmt.Sprintf("cannot create email for user with ID [%s] and for expired message with ID [%s]", payload.UserID, payload.MessageID) return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) @@ -113,7 +113,7 @@ func (service *EmailNotificationService) NotifyMessageFailed(ctx context.Context return nil } - email, err := service.factory.MessageFailed(user, payload.ID, payload.Owner, payload.Contact, payload.Content, payload.ErrorMessage) + email, err := service.factory.MessageFailed(user, payload) if err != nil { msg := fmt.Sprintf("cannot create email for user with ID [%s] for [%s] message with ID [%s]", payload.UserID, payload.ID) return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) diff --git a/api/pkg/services/google_cloud_push_queue_service.go b/api/pkg/services/google_cloud_push_queue_service.go index a15f2f44..d22dac83 100644 --- a/api/pkg/services/google_cloud_push_queue_service.go +++ b/api/pkg/services/google_cloud_push_queue_service.go @@ -6,7 +6,7 @@ import ( "net/http" "time" - "github.com/avast/retry-go" + "github.com/avast/retry-go/v5" cloudtasks "cloud.google.com/go/cloudtasks/apiv2" "cloud.google.com/go/cloudtasks/apiv2/cloudtaskspb" @@ -39,10 +39,10 @@ func NewGooglePushQueue( // Enqueue a task to the queue func (queue *googlePushQueue) Enqueue(ctx context.Context, task *PushQueueTask, timeout time.Duration) (queueID string, err error) { - err = retry.Do(func() error { + err = retry.New(retry.Attempts(3)).Do(func() error { queueID, err = queue.enqueueImpl(ctx, task, timeout) return err - }, retry.Attempts(3)) + }) return queueID, err } @@ -82,6 +82,7 @@ func (queue *googlePushQueue) enqueueImpl(ctx context.Context, task *PushQueueTa queueTask, err := queue.client.CreateTask(requestCtx, req) if err != nil { msg := fmt.Sprintf("cannot schedule task [%s] to URL [%s]", string(task.Body), task.URL) + ctxLogger.Error(stacktrace.Propagate(err, msg)) return queueID, queue.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) } diff --git a/api/pkg/services/heartbeat_service.go b/api/pkg/services/heartbeat_service.go index 24661cf9..9160923d 100644 --- a/api/pkg/services/heartbeat_service.go +++ b/api/pkg/services/heartbeat_service.go @@ -49,6 +49,25 @@ func NewHeartbeatService( } } +// DeleteAllForUser deletes all entities.Heartbeat for an entities.UserID. +func (service *HeartbeatService) DeleteAllForUser(ctx context.Context, userID entities.UserID) error { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + if err := service.repository.DeleteAllForUser(ctx, userID); err != nil { + msg := fmt.Sprintf("could not delete all [entities.Heartbeat] for user with ID [%s]", userID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err := service.monitorRepository.DeleteAllForUser(ctx, userID); err != nil { + msg := fmt.Sprintf("could not delete all [entities.HeartbeatMonitor] for user with ID [%s]", userID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("deleted all [entities.Heartbeat] and [entities.HeartbeatMonitor] for user with ID [%s]", userID)) + return nil +} + // Index fetches the heartbeats for a phone number func (service *HeartbeatService) Index(ctx context.Context, userID entities.UserID, owner string, params repositories.IndexParams) (*[]entities.Heartbeat, error) { ctx, span := service.tracer.Start(ctx) @@ -71,6 +90,7 @@ type HeartbeatStoreParams struct { Owner string Version string Charging bool + Source string Timestamp time.Time UserID entities.UserID } @@ -97,6 +117,23 @@ func (service *HeartbeatService) Store(ctx context.Context, params HeartbeatStor } ctxLogger.Info(fmt.Sprintf("heartbeat saved with id [%s] for user [%s]", heartbeat.ID, heartbeat.UserID)) + + monitor, err := service.monitorRepository.Load(ctx, params.UserID, params.Owner) + if stacktrace.GetCode(err) == repositories.ErrCodeNotFound { + ctxLogger.Info(fmt.Sprintf("heartbeat monitor does not exist for owner [%s] and user [%s]", params.Owner, params.UserID)) + return heartbeat, nil + } + if err != nil { + msg := fmt.Sprintf("cannot load heartbeat monitor for owner [%s] and user [%s]", params.Owner, params.UserID) + ctxLogger.Error(stacktrace.Propagate(err, msg)) + return heartbeat, nil + } + + if monitor.PhoneIsOffline() { + ctxLogger.Info(fmt.Sprintf("phone with monitor ID [%s] was offline for user [%s]", monitor.ID, monitor.UserID)) + service.handleHeartbeatWhenPhoneWasOffline(ctx, params.Source, heartbeat, monitor) + } + return heartbeat, nil } @@ -108,6 +145,39 @@ type HeartbeatMonitorStoreParams struct { UserID entities.UserID } +func (service *HeartbeatService) handleHeartbeatWhenPhoneWasOffline(ctx context.Context, source string, heartbeat *entities.Heartbeat, monitor *entities.HeartbeatMonitor) { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + if err := service.UpdatePhoneOnline(ctx, monitor.UserID, monitor.ID, true); err != nil { + msg := fmt.Sprintf("cannot update phone online status for heartbeat monitor [%s]", monitor.ID) + ctxLogger.Error(service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))) + } + + event, err := service.createEvent(events.EventTypePhoneHeartbeatOnline, source, &events.PhoneHeartbeatOnlinePayload{ + PhoneID: monitor.PhoneID, + UserID: monitor.UserID, + LastHeartbeatTimestamp: heartbeat.Timestamp, + Timestamp: time.Now().UTC(), + MonitorID: monitor.ID, + Owner: heartbeat.Owner, + }) + if err != nil { + msg := fmt.Sprintf("cannot create [%s] event for monitor with ID [%s]", events.EventTypePhoneHeartbeatOnline, monitor.ID) + ctxLogger.Error(service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))) + return + } + + if err = service.dispatcher.Dispatch(ctx, event); err != nil { + msg := fmt.Sprintf("cannot dispatch event [%s] for heartbeat monitor with phone id [%s]", event.Type(), monitor.PhoneID) + ctxLogger.Error(service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))) + return + } + + ctxLogger.Info(fmt.Sprintf("[%s] event created with ID [%s] for monitor ID [%s] and user [%s]", event.Type(), event.ID(), monitor.ID, monitor.UserID)) + return +} + // StoreMonitor a new entities.HeartbeatMonitor func (service *HeartbeatService) StoreMonitor(ctx context.Context, params *HeartbeatMonitorStoreParams) (*entities.HeartbeatMonitor, error) { ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) @@ -148,12 +218,13 @@ func (service *HeartbeatService) phoneMonitor(ctx context.Context, params *Heart monitor, err := service.monitorRepository.Load(ctx, params.UserID, params.Owner) if stacktrace.GetCode(err) == repositories.ErrCodeNotFound { monitor = &entities.HeartbeatMonitor{ - ID: uuid.New(), - PhoneID: params.PhoneID, - UserID: params.UserID, - Owner: params.Owner, - CreatedAt: time.Now().UTC(), - UpdatedAt: time.Now().UTC(), + ID: uuid.New(), + PhoneID: params.PhoneID, + UserID: params.UserID, + Owner: params.Owner, + PhoneOnline: true, + CreatedAt: time.Now().UTC(), + UpdatedAt: time.Now().UTC(), } if err = service.monitorRepository.Store(ctx, monitor); err != nil { @@ -189,6 +260,22 @@ func (service *HeartbeatService) DeleteMonitor(ctx context.Context, userID entit return nil } +// UpdatePhoneOnline updates the phone_online field in an entities.HeartbeatMonitor +func (service *HeartbeatService) UpdatePhoneOnline(ctx context.Context, userID entities.UserID, monitorID uuid.UUID, phoneOnline bool) error { + ctx, span := service.tracer.Start(ctx) + defer span.End() + + ctxLogger := service.tracer.CtxLogger(service.logger, span) + + if err := service.monitorRepository.UpdatePhoneOnline(ctx, userID, monitorID, phoneOnline); err != nil { + msg := fmt.Sprintf("cannot update heartbeat monitor [%s] with userID [%s] and status [%t]", monitorID, userID, phoneOnline) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("heartbeat monitor [%s] updated for userID [%s] and status [%t]", monitorID, userID, phoneOnline)) + return nil +} + // HeartbeatMonitorParams are parameters for monitoring the heartbeat type HeartbeatMonitorParams struct { Owner string @@ -205,17 +292,21 @@ func (service *HeartbeatService) Monitor(ctx context.Context, params *HeartbeatM ctxLogger := service.tracer.CtxLogger(service.logger, span) - exists, err := service.monitorRepository.Exists(ctx, params.UserID, params.MonitorID) + monitor, err := service.monitorRepository.Load(ctx, params.UserID, params.Owner) + if err != nil && stacktrace.GetCode(err) == repositories.ErrCodeNotFound { + ctxLogger.Info(fmt.Sprintf("heartbeat monitor does not exist for owner [%s] and user [%s]", params.Owner, params.UserID)) + return nil + } + if err != nil { msg := fmt.Sprintf("cannot check if monitor exists with userID [%s] and owner [%s]", params.UserID, params.Owner) ctxLogger.Error(stacktrace.Propagate(err, msg)) return service.scheduleHeartbeatCheck(ctx, time.Now().UTC(), params) } - if !exists { - ctxLogger.Info(fmt.Sprintf("heartbeat monitor does not exist for owner [%s] and user [%s]", params.Owner, params.UserID)) - return nil - } + // Update params in case of ID duplicate + params.PhoneID = monitor.PhoneID + params.MonitorID = monitor.ID heartbeat, err := service.repository.Last(ctx, params.UserID, params.Owner) if err != nil { @@ -231,7 +322,7 @@ func (service *HeartbeatService) Monitor(ctx context.Context, params *HeartbeatM } if time.Now().UTC().Sub(heartbeat.Timestamp) > (heartbeatCheckInterval*4) && - time.Now().UTC().Sub(heartbeat.Timestamp) < (heartbeatCheckInterval*5) { + time.Now().UTC().Sub(heartbeat.Timestamp) < (heartbeatCheckInterval*5) && monitor.PhoneOnline { return service.handleFailedMonitor(ctx, heartbeat.Timestamp, params) } @@ -272,7 +363,7 @@ func (service *HeartbeatService) handleFailedMonitor(ctx context.Context, lastTi return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) } - event, err := service.createPhoneHeartbeatDeadEvent(params.Source, &events.PhoneHeartbeatDeadPayload{ + event, err := service.createPhoneHeartbeatOfflineEvent(params.Source, &events.PhoneHeartbeatOfflinePayload{ PhoneID: params.PhoneID, UserID: params.UserID, MonitorID: params.MonitorID, @@ -330,8 +421,8 @@ func (service *HeartbeatService) createPhoneHeartbeatMissedEvent(source string, return service.createEvent(events.PhoneHeartbeatMissed, source, payload) } -func (service *HeartbeatService) createPhoneHeartbeatDeadEvent(source string, payload *events.PhoneHeartbeatDeadPayload) (cloudevents.Event, error) { - return service.createEvent(events.EventTypePhoneHeartbeatDead, source, payload) +func (service *HeartbeatService) createPhoneHeartbeatOfflineEvent(source string, payload *events.PhoneHeartbeatOfflinePayload) (cloudevents.Event, error) { + return service.createEvent(events.EventTypePhoneHeartbeatOffline, source, payload) } func (service *HeartbeatService) createPhoneHeartbeatCheckEvent(source string, payload *events.PhoneHeartbeatCheckPayload) (cloudevents.Event, error) { diff --git a/api/pkg/services/integration_3cx_service.go b/api/pkg/services/integration_3cx_service.go index d6366bf3..3859111c 100644 --- a/api/pkg/services/integration_3cx_service.go +++ b/api/pkg/services/integration_3cx_service.go @@ -43,6 +43,20 @@ func NewIntegration3CXService( } } +// DeleteAllForUser deletes all entities.Integration3CX for an entities.UserID. +func (service *Integration3CXService) DeleteAllForUser(ctx context.Context, userID entities.UserID) error { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + if err := service.repository.DeleteAllForUser(ctx, userID); err != nil { + msg := fmt.Sprintf("could not delete all [entities.Integration3CX] for user with ID [%s]", userID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("deleted all [entities.Integration3CX] for user with ID [%s]", userID)) + return nil +} + // Send an event to a 3CX webhook func (service *Integration3CXService) Send(ctx context.Context, userID entities.UserID, event cloudevents.Event) error { ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) diff --git a/api/pkg/services/lemonsqueezy_service.go b/api/pkg/services/lemonsqueezy_service.go index 736dbdb6..96161eda 100644 --- a/api/pkg/services/lemonsqueezy_service.go +++ b/api/pkg/services/lemonsqueezy_service.go @@ -11,7 +11,7 @@ import ( "github.com/NdoleStudio/httpsms/pkg/entities" "github.com/NdoleStudio/httpsms/pkg/events" "github.com/NdoleStudio/httpsms/pkg/telemetry" - lemonsqueezy "github.com/NdoleStudio/lemonsqueezy-go" + "github.com/NdoleStudio/lemonsqueezy-go" "github.com/palantir/stacktrace" ) @@ -39,13 +39,39 @@ func NewLemonsqueezyService( } } +// GetUserID gets the user ID from the request +func (service *LemonsqueezyService) GetUserID(ctx context.Context, request *lemonsqueezy.WebhookRequestSubscription) (entities.UserID, error) { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + userID, ok := request.Meta.CustomData["user_id"].(string) + if ok { + return entities.UserID(userID), nil + } + + ctxLogger.Info(fmt.Sprintf("user_id not found in request meta data. Searching via email [%s]", request.Data.Attributes.UserEmail)) + user, err := service.userRepository.LoadByEmail(ctx, request.Data.Attributes.UserEmail) + if err != nil { + msg := fmt.Sprintf("cannot load user with email [%s]", request.Data.Attributes.UserEmail) + return "", service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return user.ID, nil +} + // HandleSubscriptionCreatedEvent handles the subscription_created lemonsqueezy event func (service *LemonsqueezyService) HandleSubscriptionCreatedEvent(ctx context.Context, source string, request *lemonsqueezy.WebhookRequestSubscription) error { ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) defer span.End() + userID, err := service.GetUserID(ctx, request) + if err != nil { + msg := fmt.Sprintf("cannot get user ID for subscription created event with ID [%s]", request.Data.ID) + return stacktrace.Propagate(err, msg) + } + payload := &events.UserSubscriptionCreatedPayload{ - UserID: entities.UserID(request.Meta.CustomData["user_id"].(string)), + UserID: userID, SubscriptionCreatedAt: request.Data.Attributes.CreatedAt, SubscriptionID: request.Data.ID, SubscriptionName: service.subscriptionName(request.Data.Attributes.VariantName), @@ -101,6 +127,41 @@ func (service *LemonsqueezyService) HandleSubscriptionCanceledEvent(ctx context. return nil } +// HandleSubscriptionUpdatedEvent handles the subscription_cancelled lemonsqueezy event +func (service *LemonsqueezyService) HandleSubscriptionUpdatedEvent(ctx context.Context, source string, request *lemonsqueezy.WebhookRequestSubscription) error { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + user, err := service.userRepository.LoadBySubscriptionID(ctx, request.Data.ID) + if err != nil { + msg := fmt.Sprintf("cannot load user with subscription ID [%s]", request.Data.ID) + return stacktrace.Propagate(err, msg) + } + + payload := &events.UserSubscriptionUpdatedPayload{ + UserID: user.ID, + SubscriptionUpdatedAt: request.Data.Attributes.UpdatedAt, + SubscriptionID: request.Data.ID, + SubscriptionName: service.subscriptionName(request.Data.Attributes.VariantName), + SubscriptionEndsAt: request.Data.Attributes.EndsAt, + SubscriptionRenewsAt: request.Data.Attributes.RenewsAt, + SubscriptionStatus: request.Data.Attributes.Status, + } + + event, err := service.createEvent(events.UserSubscriptionUpdated, source, payload) + if err != nil { + msg := fmt.Sprintf("cannot created [%s] event for user [%s]", events.UserSubscriptionUpdated, payload.UserID) + return stacktrace.Propagate(err, msg) + } + + if err = service.eventDispatcher.Dispatch(ctx, event); err != nil { + msg := fmt.Sprintf("cannot dispatch [%s] event for user [%s]", event.Type(), payload.UserID) + return stacktrace.Propagate(err, msg) + } + ctxLogger.Info(fmt.Sprintf("[%s] subscription [%s] updated for user [%s]", payload.SubscriptionName, payload.SubscriptionID, payload.UserID)) + return nil +} + // HandleSubscriptionExpiredEvent handles the subscription_expired lemonsqueezy event func (service *LemonsqueezyService) HandleSubscriptionExpiredEvent(ctx context.Context, source string, request *lemonsqueezy.WebhookRequestSubscription) error { ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) @@ -162,7 +223,18 @@ func (service *LemonsqueezyService) subscriptionName(variant string) entities.Su if strings.Contains(strings.ToLower(variant), "monthly") { return entities.SubscriptionName100KMonthly } - return entities.SubscriptionName100KYearly + } + + if strings.Contains(strings.ToLower(variant), "50k") { + if strings.Contains(strings.ToLower(variant), "monthly") { + return entities.SubscriptionName50KMonthly + } + } + + if strings.Contains(strings.ToLower(variant), "200k") { + if strings.Contains(strings.ToLower(variant), "monthly") { + return entities.SubscriptionName200KMonthly + } } return entities.SubscriptionNameFree diff --git a/api/pkg/services/marketting_service.go b/api/pkg/services/marketting_service.go index b6513dec..199eb81c 100644 --- a/api/pkg/services/marketting_service.go +++ b/api/pkg/services/marketting_service.go @@ -2,38 +2,25 @@ package services import ( "context" - "encoding/json" "fmt" - "log" "strings" - "github.com/sendgrid/sendgrid-go" + semconv "go.opentelemetry.io/otel/semconv/v1.10.0" "firebase.google.com/go/auth" "github.com/NdoleStudio/httpsms/pkg/entities" "github.com/NdoleStudio/httpsms/pkg/telemetry" - "github.com/davecgh/go-spew/spew" + plunk "github.com/NdoleStudio/plunk-go" + "github.com/gofiber/fiber/v2" "github.com/palantir/stacktrace" ) // MarketingService is handles marketing requests type MarketingService struct { - logger telemetry.Logger - tracer telemetry.Tracer - authClient *auth.Client - sendgridAPIKey string - sendgridListID string -} - -type sendgridContact struct { - FirstName string `json:"first_name"` - LastName string `json:"last_name"` - Email string `json:"email"` -} - -type sendgridContactRequest struct { - ListIDs []string `json:"list_ids"` - Contacts []sendgridContact `json:"contacts"` + logger telemetry.Logger + tracer telemetry.Tracer + authClient *auth.Client + plunkClient *plunk.Client } // NewMarketingService creates a new instance of the MarketingService @@ -41,102 +28,85 @@ func NewMarketingService( logger telemetry.Logger, tracer telemetry.Tracer, authClient *auth.Client, - sendgridAPIKey string, - sendgridListID string, + plunkClient *plunk.Client, ) *MarketingService { return &MarketingService{ - logger: logger.WithService(fmt.Sprintf("%T", &MarketingService{})), - tracer: tracer, - authClient: authClient, - sendgridAPIKey: sendgridAPIKey, - sendgridListID: sendgridListID, + logger: logger.WithService(fmt.Sprintf("%T", &MarketingService{})), + tracer: tracer, + authClient: authClient, + plunkClient: plunkClient, } } -// AddToList adds a new user on the onboarding automation. -func (service *MarketingService) AddToList(ctx context.Context, user *entities.User) { +// DeleteContact a user if exists as a contact +func (service *MarketingService) DeleteContact(ctx context.Context, email string) error { ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) defer span.End() - userRecord, err := service.authClient.GetUser(ctx, string(user.ID)) + response, _, err := service.plunkClient.Contacts.List(ctx, map[string]string{"search": email}) if err != nil { - msg := fmt.Sprintf("cannot get auth user with id [%s]", user.ID) - ctxLogger.Error(stacktrace.Propagate(err, msg)) - return + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, fmt.Sprintf("cannot search for contact with email [%s]", email))) } - id, err := service.addContact(sendgridContactRequest{ - ListIDs: []string{service.sendgridListID}, - Contacts: []sendgridContact{service.toSendgridContact(userRecord)}, - }) - if err != nil { - msg := fmt.Sprintf("cannot add user with id [%s] to list [%s]", user.ID, service.sendgridListID) - ctxLogger.Error(stacktrace.Propagate(err, msg)) - return + if len(response.Data) == 0 { + ctxLogger.Info(fmt.Sprintf("no contact found with email [%s], skipping deletion", email)) + return nil } - ctxLogger.Info(fmt.Sprintf("user [%s] added to list [%s] with job [%s]", user.ID, service.sendgridListID, id)) + contact := response.Data[0] + if _, err = service.plunkClient.Contacts.Delete(ctx, contact.ID); err != nil { + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, fmt.Sprintf("cannot delete user with ID [%s] from contacts", contact.Data[string(semconv.EnduserIDKey)]))) + } + + ctxLogger.Info(fmt.Sprintf("deleted user with ID [%s] from as marketting contact with ID [%s]", contact.Data[string(semconv.EnduserIDKey)], contact.ID)) + return nil } -// DeleteContacts deletes contacts from sendgrid -func (service *MarketingService) DeleteContacts(ctx context.Context, contactIDs []string) error { +// CreateContact adds a new user on the onboarding automation. +func (service *MarketingService) CreateContact(ctx context.Context, userID entities.UserID) error { ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) defer span.End() - request := sendgrid.GetRequest(service.sendgridAPIKey, "/v3/marketing/contacts", "https://api.sendgrid.com") - request.Method = "DELETE" - request.QueryParams = map[string]string{ - "ids": strings.Join(contactIDs, ","), + userRecord, err := service.authClient.GetUser(ctx, userID.String()) + if err != nil { + msg := fmt.Sprintf("cannot get auth user with id [%s]", userID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) } - response, err := sendgrid.API(request) + data := service.attributes(userRecord) + data[string(semconv.ServiceNameKey)] = "httpsms.com" + data[string(semconv.EnduserIDKey)] = userRecord.UID + + event, _, err := service.plunkClient.Tracker.TrackEvent(ctx, &plunk.TrackEventRequest{ + Email: userRecord.Email, + Event: "contact.created", + Subscribed: true, + Data: data, + }) if err != nil { - return stacktrace.Propagate(err, fmt.Sprintf("cannot delete contacts in a sendgrid list [%s]", service.sendgridListID)) + msg := fmt.Sprintf("cannot create contact for user with id [%s]", userID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) } - ctxLogger.Info(spew.Sdump(response.Body)) + ctxLogger.Info(fmt.Sprintf("user [%s] added to marketting list with contact ID [%s] and event ID [%s]", userID, event.Data.Contact, event.Data.Event)) return nil } -func (service *MarketingService) toSendgridContact(user *auth.UserRecord) sendgridContact { +func (service *MarketingService) attributes(user *auth.UserRecord) map[string]any { name := strings.TrimSpace(user.DisplayName) if name == "" { - return sendgridContact{ - FirstName: "", - LastName: "", - Email: user.Email, - } + return fiber.Map{} } parts := strings.Split(name, " ") if len(parts) == 1 { - return sendgridContact{ - FirstName: name, - LastName: "", - Email: user.Email, + return fiber.Map{ + "firstName": name, } } - return sendgridContact{ - FirstName: strings.Join(parts[0:len(parts)-1], " "), - LastName: parts[len(parts)-1], - Email: user.Email, - } -} - -func (service *MarketingService) addContact(contactRequest sendgridContactRequest) (string, error) { - request := sendgrid.GetRequest(service.sendgridAPIKey, "/v3/marketing/contacts", "https://api.sendgrid.com") - request.Method = "PUT" - - body, err := json.Marshal(contactRequest) - if err != nil { - log.Fatal(stacktrace.Propagate(err, fmt.Sprintf("cannot marshal [%s]", spew.Sdump(contactRequest)))) - } - - request.Body = body - response, err := sendgrid.API(request) - if err != nil { - return "", stacktrace.Propagate(err, fmt.Sprintf("cannot add contact to sendgrid list [%s]", spew.Sdump(contactRequest))) + return fiber.Map{ + "firstName": strings.Join(parts[0:len(parts)-1], " "), + "lastName": parts[len(parts)-1], } - return response.Body, nil } diff --git a/api/pkg/services/message_service.go b/api/pkg/services/message_service.go index d78273f0..131c3520 100644 --- a/api/pkg/services/message_service.go +++ b/api/pkg/services/message_service.go @@ -2,12 +2,14 @@ package services import ( "context" + "encoding/base64" "fmt" + "strings" "time" "github.com/davecgh/go-spew/spew" - "github.com/nyaruka/phonenumbers" + "golang.org/x/sync/errgroup" "github.com/NdoleStudio/httpsms/pkg/events" "github.com/NdoleStudio/httpsms/pkg/repositories" @@ -19,14 +21,23 @@ import ( "github.com/NdoleStudio/httpsms/pkg/telemetry" ) +// ServiceAttachment represents attachment data passed to the service layer +type ServiceAttachment struct { + Name string + ContentType string + Content string // base64-encoded +} + // MessageService is handles message requests type MessageService struct { service - logger telemetry.Logger - tracer telemetry.Tracer - eventDispatcher *EventDispatcher - phoneService *PhoneService - repository repositories.MessageRepository + logger telemetry.Logger + tracer telemetry.Tracer + eventDispatcher *EventDispatcher + phoneService *PhoneService + repository repositories.MessageRepository + attachmentRepository repositories.AttachmentRepository + apiBaseURL string } // NewMessageService creates a new MessageService @@ -36,22 +47,27 @@ func NewMessageService( repository repositories.MessageRepository, eventDispatcher *EventDispatcher, phoneService *PhoneService, + attachmentRepository repositories.AttachmentRepository, + apiBaseURL string, ) (s *MessageService) { return &MessageService{ - logger: logger.WithService(fmt.Sprintf("%T", s)), - tracer: tracer, - repository: repository, - phoneService: phoneService, - eventDispatcher: eventDispatcher, + logger: logger.WithService(fmt.Sprintf("%T", s)), + tracer: tracer, + repository: repository, + phoneService: phoneService, + eventDispatcher: eventDispatcher, + attachmentRepository: attachmentRepository, + apiBaseURL: apiBaseURL, } } // MessageGetOutstandingParams parameters for sending a new message type MessageGetOutstandingParams struct { - Source string - UserID entities.UserID - Timestamp time.Time - MessageID uuid.UUID + Source string + UserID entities.UserID + PhoneNumbers []string + Timestamp time.Time + MessageID uuid.UUID } // GetOutstanding fetches messages that still to be sent to the phone @@ -61,7 +77,7 @@ func (service *MessageService) GetOutstanding(ctx context.Context, params Messag ctxLogger := service.tracer.CtxLogger(service.logger, span) - message, err := service.repository.GetOutstanding(ctx, params.UserID, params.MessageID) + message, err := service.repository.GetOutstanding(ctx, params.UserID, params.MessageID, params.PhoneNumbers) if err != nil { msg := fmt.Sprintf("could not fetch outstanding messages with params [%s]", spew.Sdump(params)) return nil, service.tracer.WrapErrorSpan(span, stacktrace.PropagateWithCode(err, stacktrace.GetCode(err), msg)) @@ -72,6 +88,7 @@ func (service *MessageService) GetOutstanding(ctx context.Context, params Messag Owner: message.Owner, Contact: message.Contact, Timestamp: params.Timestamp, + Encrypted: message.Encrypted, UserID: message.UserID, Content: message.Content, SIM: message.SIM, @@ -92,6 +109,125 @@ func (service *MessageService) GetOutstanding(ctx context.Context, params Messag return message, nil } +// DeleteAllForUser deletes all entities.Message for an entities.UserID. +func (service *MessageService) DeleteAllForUser(ctx context.Context, userID entities.UserID) error { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + if err := service.repository.DeleteAllForUser(ctx, userID); err != nil { + msg := fmt.Sprintf("could not delete [entities.Message] for user with ID [%s]", userID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("deleted all [entities.Message] for user with ID [%s]", userID)) + return nil +} + +// DeleteMessage deletes a message from the database +func (service *MessageService) DeleteMessage(ctx context.Context, source string, message *entities.Message) error { + ctx, span := service.tracer.Start(ctx) + defer span.End() + + ctxLogger := service.tracer.CtxLogger(service.logger, span) + + if err := service.repository.Delete(ctx, message.UserID, message.ID); err != nil { + msg := fmt.Sprintf("could not delete message with ID [%s] for user wit ID [%s]", message.ID, message.UserID) + return service.tracer.WrapErrorSpan(span, stacktrace.PropagateWithCode(err, stacktrace.GetCode(err), msg)) + } + + var prevID *uuid.UUID + var prevStatus *entities.MessageStatus + var prevContent *string + previousMessage, err := service.repository.LastMessage(ctx, message.UserID, message.Owner, message.Contact) + if err == nil { + prevID = &previousMessage.ID + prevStatus = &previousMessage.Status + prevContent = &previousMessage.Content + } + + event, err := service.createEvent(events.MessageAPIDeleted, source, &events.MessageAPIDeletedPayload{ + MessageID: message.ID, + UserID: message.UserID, + Owner: message.Owner, + Encrypted: message.Encrypted, + RequestID: message.RequestID, + Contact: message.Contact, + Timestamp: time.Now().UTC(), + Content: message.Content, + PreviousMessageID: prevID, + PreviousMessageStatus: prevStatus, + PreviousMessageContent: prevContent, + SIM: message.SIM, + }) + if err != nil { + msg := fmt.Sprintf("cannot create [%T] for message with ID [%s]", event, message.ID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("created event [%s] with id [%s] for message [%s]", event.Type(), event.ID(), message.ID)) + if err = service.eventDispatcher.Dispatch(ctx, event); err != nil { + msg := fmt.Sprintf("cannot dispatch event [%s] with id [%s] for message [%s]", event.Type(), event.ID(), message.ID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("dispatched event [%s] with id [%s] for message [%s]", event.Type(), event.ID(), message.ID)) + return nil +} + +// DeleteByOwnerAndContact deletes all the messages between an owner and a contact +func (service *MessageService) DeleteByOwnerAndContact(ctx context.Context, userID entities.UserID, owner, contact string) error { + ctx, span := service.tracer.Start(ctx) + defer span.End() + + ctxLogger := service.tracer.CtxLogger(service.logger, span) + + if err := service.repository.DeleteByOwnerAndContact(ctx, userID, owner, contact); err != nil { + msg := fmt.Sprintf("could not all delete messages for user with ID [%s] between owner [%s] and contact [%s] ", userID, owner, contact) + return service.tracer.WrapErrorSpan(span, stacktrace.PropagateWithCode(err, stacktrace.GetCode(err), msg)) + } + + ctxLogger.Info(fmt.Sprintf("deleted all messages for user with ID [%s] between owner [%s] and contact [%s] ", userID, owner, contact)) + return nil +} + +// RespondToMissedCall creates an SMS response to a missed phone call on the android phone +func (service *MessageService) RespondToMissedCall(ctx context.Context, source string, payload *events.MessageCallMissedPayload) error { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + phone, err := service.phoneService.Load(ctx, payload.UserID, payload.Owner) + if err != nil { + msg := fmt.Sprintf("cannot find phone with owner [%s] for user with ID [%s] when handling missed phone call message [%s]", payload.Owner, payload.UserID, payload.MessageID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if phone.MissedCallAutoReply == nil || strings.TrimSpace(*phone.MissedCallAutoReply) == "" { + ctxLogger.Info(fmt.Sprintf("no auto reply set for phone [%s] for message [%s] with user [%s]", payload.Owner, payload.MessageID, payload.UserID)) + return nil + } + + requestID := fmt.Sprintf("missed-call-%s", payload.MessageID) + owner, _ := phonenumbers.Parse(payload.Owner, phonenumbers.UNKNOWN_REGION) + message, err := service.SendMessage(ctx, MessageSendParams{ + Owner: owner, + Contact: payload.Contact, + Encrypted: false, + Content: *phone.MissedCallAutoReply, + Source: source, + SendAt: nil, + RequestID: &requestID, + UserID: payload.UserID, + RequestReceivedAt: time.Now().UTC(), + }) + if err != nil { + msg := fmt.Sprintf("cannot send auto response message for owner [%s] for user with ID [%s] when handling missed phone call message [%s]", payload.Owner, payload.UserID, payload.MessageID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("created response message with ID [%s] for missed call event [%s] for user [%s]", message.ID, payload.MessageID, message.UserID)) + return nil +} + // MessageGetParams parameters for sending a new message type MessageGetParams struct { repositories.IndexParams @@ -168,30 +304,43 @@ func (service *MessageService) StoreEvent(ctx context.Context, message *entities // MessageReceiveParams parameters registering a message event type MessageReceiveParams struct { - Contact string - UserID entities.UserID - Owner phonenumbers.PhoneNumber - Content string - SIM entities.SIM - Timestamp time.Time - Source string + Contact string + UserID entities.UserID + Owner phonenumbers.PhoneNumber + Content string + SIM entities.SIM + Timestamp time.Time + Encrypted bool + Source string + Attachments []ServiceAttachment } // ReceiveMessage handles message received by a mobile phone -func (service *MessageService) ReceiveMessage(ctx context.Context, params MessageReceiveParams) (*entities.Message, error) { +func (service *MessageService) ReceiveMessage(ctx context.Context, params *MessageReceiveParams) (*entities.Message, error) { ctx, span := service.tracer.Start(ctx) defer span.End() ctxLogger := service.tracer.CtxLogger(service.logger, span) + messageID := uuid.New() + + ctxLogger.Info(fmt.Sprintf("uploading [%d] attachments for user [%s] message [%s]", len(params.Attachments), params.UserID, messageID)) + attachmentURLs, err := service.uploadAttachments(ctx, params.UserID, messageID, params.Attachments) + if err != nil { + msg := fmt.Sprintf("cannot upload attachments for user [%s] message [%s]", params.UserID, messageID) + return nil, service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + eventPayload := events.MessagePhoneReceivedPayload{ - MessageID: uuid.New(), - UserID: params.UserID, - Owner: phonenumbers.Format(¶ms.Owner, phonenumbers.E164), - Contact: params.Contact, - Timestamp: params.Timestamp, - Content: params.Content, - SIM: params.SIM, + MessageID: messageID, + UserID: params.UserID, + Encrypted: params.Encrypted, + Owner: phonenumbers.Format(¶ms.Owner, phonenumbers.E164), + Contact: params.Contact, + Timestamp: params.Timestamp, + Content: params.Content, + SIM: params.SIM, + Attachments: attachmentURLs, } ctxLogger.Info(fmt.Sprintf("creating cloud event for received with ID [%s]", eventPayload.MessageID)) @@ -208,7 +357,7 @@ func (service *MessageService) ReceiveMessage(ctx context.Context, params Messag msg := fmt.Sprintf("cannot dispatch event type [%s] and id [%s]", event.Type(), event.ID()) return nil, service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) } - ctxLogger.Info(fmt.Sprintf("event [%s] dispatched succesfully", event.ID())) + ctxLogger.Info(fmt.Sprintf("event [%s] dispatched successfully", event.ID())) return service.storeReceivedMessage(ctx, eventPayload) } @@ -224,6 +373,7 @@ func (service *MessageService) handleMessageSentEvent(ctx context.Context, param RequestID: message.RequestID, Timestamp: params.Timestamp, Contact: message.Contact, + Encrypted: message.Encrypted, Content: message.Content, SIM: message.SIM, }) @@ -249,6 +399,7 @@ func (service *MessageService) handleMessageDeliveredEvent(ctx context.Context, UserID: message.UserID, RequestID: message.RequestID, Timestamp: params.Timestamp, + Encrypted: message.Encrypted, Contact: message.Contact, Content: message.Content, SIM: message.SIM, @@ -258,8 +409,8 @@ func (service *MessageService) handleMessageDeliveredEvent(ctx context.Context, return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) } - if err = service.eventDispatcher.Dispatch(ctx, event); err != nil { - msg := fmt.Sprintf("cannot dispatch event type [%s] and id [%s]", event.Type(), event.ID()) + if _, err = service.eventDispatcher.DispatchWithTimeout(ctx, event, time.Second); err != nil { + msg := fmt.Sprintf("cannot dispatch event type [%s] and id [%s] for message [%s]", event.Type(), event.ID(), message.ID) return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) } return nil @@ -271,7 +422,7 @@ func (service *MessageService) handleMessageFailedEvent(ctx context.Context, par errorMessage := "UNKNOWN ERROR" if params.ErrorMessage != nil { - errorMessage = *params.ErrorMessage + errorMessage = service.enrichErrorMessage(*params.ErrorMessage) } event, err := service.createMessageSendFailedEvent(params.Source, events.MessageSendFailedPayload{ @@ -279,6 +430,7 @@ func (service *MessageService) handleMessageFailedEvent(ctx context.Context, par Owner: message.Owner, ErrorMessage: errorMessage, Timestamp: params.Timestamp, + Encrypted: message.Encrypted, Contact: message.Contact, RequestID: message.RequestID, UserID: message.UserID, @@ -301,7 +453,9 @@ func (service *MessageService) handleMessageFailedEvent(ctx context.Context, par type MessageSendParams struct { Owner *phonenumbers.PhoneNumber Contact string + Encrypted bool Content string + Attachments []string Source string SendAt *time.Time RequestID *string @@ -321,12 +475,14 @@ func (service *MessageService) SendMessage(ctx context.Context, params MessageSe eventPayload := events.MessageAPISentPayload{ MessageID: uuid.New(), UserID: params.UserID, + Encrypted: params.Encrypted, MaxSendAttempts: sendAttempts, RequestID: params.RequestID, Owner: phonenumbers.Format(params.Owner, phonenumbers.E164), Contact: params.Contact, RequestReceivedAt: params.RequestReceivedAt, Content: params.Content, + Attachments: params.Attachments, ScheduledSendTime: params.SendAt, SIM: sim, } @@ -354,6 +510,55 @@ func (service *MessageService) SendMessage(ctx context.Context, params MessageSe return message, err } +// MissedCallParams parameters for sending a new message +type MissedCallParams struct { + Owner *phonenumbers.PhoneNumber + Contact string + Source string + SIM entities.SIM + Timestamp time.Time + UserID entities.UserID +} + +// RegisterMissedCall a new message +func (service *MessageService) RegisterMissedCall(ctx context.Context, params *MissedCallParams) (*entities.Message, error) { + ctx, span := service.tracer.Start(ctx) + defer span.End() + + ctxLogger := service.tracer.CtxLogger(service.logger, span) + + eventPayload := &events.MessageCallMissedPayload{ + MessageID: uuid.New(), + UserID: params.UserID, + Timestamp: params.Timestamp, + Owner: phonenumbers.Format(params.Owner, phonenumbers.E164), + Contact: params.Contact, + SIM: params.SIM, + } + + event, err := service.createEvent(events.MessageCallMissed, params.Source, eventPayload) + if err != nil { + msg := fmt.Sprintf("cannot create [%T] from payload with message id [%s]", event, eventPayload.MessageID) + return nil, service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("created event [%s] with id [%s] and message id [%s] and user [%s]", event.Type(), event.ID(), eventPayload.MessageID, eventPayload.UserID)) + + message, err := service.storeMissedCallMessage(ctx, eventPayload) + if err != nil { + msg := fmt.Sprintf("cannot store missed call message message with id [%s]", eventPayload.MessageID) + return nil, service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err = service.eventDispatcher.Dispatch(ctx, event); err != nil { + msg := fmt.Sprintf("cannot dispatch event type [%s] and id [%s]", event.Type(), event.ID()) + return nil, service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("[%s] event with ID [%s] dispatched succesfully for message [%s] with user [%s]", event.Type(), event.ID(), eventPayload.MessageID, eventPayload.UserID)) + return message, err +} + func (service *MessageService) getSendDelay(ctxLogger telemetry.Logger, eventPayload events.MessageAPISentPayload, sendAt *time.Time) time.Duration { if sendAt == nil { return time.Duration(0) @@ -381,7 +586,9 @@ func (service *MessageService) storeReceivedMessage(ctx context.Context, params UserID: params.UserID, Contact: params.Contact, Content: params.Content, + Attachments: params.Attachments, SIM: params.SIM, + Encrypted: params.Encrypted, Type: entities.MessageTypeMobileOriginated, Status: entities.MessageStatusReceived, RequestReceivedAt: params.Timestamp, @@ -400,6 +607,55 @@ func (service *MessageService) storeReceivedMessage(ctx context.Context, params return message, nil } +func (service *MessageService) uploadAttachments(ctx context.Context, userID entities.UserID, messageID uuid.UUID, attachments []ServiceAttachment) ([]string, error) { + if len(attachments) == 0 { + return []string{}, nil + } + + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + g, gCtx := errgroup.WithContext(ctx) + urls := make([]string, len(attachments)) + paths := make([]string, len(attachments)) + + for i, attachment := range attachments { + i, attachment := i, attachment + g.Go(func() error { + decoded, err := base64.StdEncoding.DecodeString(attachment.Content) + if err != nil { + return stacktrace.Propagate(err, fmt.Sprintf("cannot decode base64 content for attachment [%d]", i)) + } + + sanitizedName := repositories.SanitizeFilename(attachment.Name, i) + ext := repositories.ExtensionFromContentType(attachment.ContentType) + filename := sanitizedName + ext + + path := fmt.Sprintf("attachments/%s/%s/%d/%s", userID, messageID, i, filename) + paths[i] = path + + if err = service.attachmentRepository.Upload(gCtx, path, decoded, attachment.ContentType); err != nil { + return stacktrace.Propagate(err, fmt.Sprintf("cannot upload attachment [%d] to path [%s]", i, path)) + } + + urls[i] = fmt.Sprintf("%s/v1/attachments/%s/%s/%d/%s", service.apiBaseURL, userID, messageID, i, filename) + ctxLogger.Info(fmt.Sprintf("uploaded attachment [%d] to [%s]", i, path)) + return nil + }) + } + + if err := g.Wait(); err != nil { + for _, path := range paths { + if path != "" { + _ = service.attachmentRepository.Delete(ctx, path) + } + } + return nil, stacktrace.Propagate(err, "cannot upload attachments") + } + + return urls, nil +} + // HandleMessageParams are parameters for handling a message event type HandleMessageParams struct { ID uuid.UUID @@ -448,8 +704,13 @@ func (service *MessageService) HandleMessageSent(ctx context.Context, params Han return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) } - if !message.IsSending() && !message.IsExpired() { - msg := fmt.Sprintf("message has wrong status [%s]. expected [%s, %s]", message.Status, entities.MessageStatusSending, entities.MessageStatusExpired) + if message.IsSent() || message.IsDelivered() { + ctxLogger.Info(fmt.Sprintf("message [%s] for [%s] has already been processed with status [%s]", message.ID, message.UserID, message.Status)) + return nil + } + + if !message.IsSending() && !message.IsExpired() && !message.IsScheduled() { + msg := fmt.Sprintf("message has wrong status [%s]. expected [%s, %s, %s]", message.Status, entities.MessageStatusSending, entities.MessageStatusExpired, entities.MessageStatusScheduled) return service.tracer.WrapErrorSpan(span, stacktrace.NewError(msg)) } @@ -586,8 +847,8 @@ func (service *MessageService) HandleMessageExpired(ctx context.Context, params return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) } - if !message.IsSending() && !message.IsScheduled() { - msg := fmt.Sprintf("message has wrong status [%s]. expected [%s, %s, %s]", message.Status, entities.MessageStatusSending, entities.MessageStatusScheduled) + if !message.IsSending() && !message.IsScheduled() && !message.IsPending() { + msg := fmt.Sprintf("message has wrong status [%s]. expected [%s, %s, %s]", message.Status, entities.MessageStatusSending, entities.MessageStatusScheduled, entities.MessageStatusPending) return service.tracer.WrapErrorSpan(span, stacktrace.NewError(msg)) } @@ -607,6 +868,7 @@ func (service *MessageService) HandleMessageExpired(ctx context.Context, params Timestamp: time.Now().UTC(), Contact: message.Contact, Owner: message.Owner, + Encrypted: message.Encrypted, UserID: message.UserID, Content: message.Content, SIM: message.SIM, @@ -681,6 +943,11 @@ func (service *MessageService) CheckExpired(ctx context.Context, params MessageC ctxLogger := service.tracer.CtxLogger(service.logger, span) message, err := service.repository.Load(ctx, params.UserID, params.MessageID) + if stacktrace.GetCode(err) == repositories.ErrCodeNotFound { + ctxLogger.Info(fmt.Sprintf("message has been deleted for userID [%s] and messageID [%s]", params.UserID, params.MessageID)) + return nil + } + if err != nil { msg := fmt.Sprintf("cannot load message with userID [%s] and messageID [%s]", params.UserID, params.MessageID) return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) @@ -695,6 +962,7 @@ func (service *MessageService) CheckExpired(ctx context.Context, params MessageC MessageID: message.ID, Owner: message.Owner, Contact: message.Contact, + Encrypted: message.Encrypted, RequestID: message.RequestID, IsFinal: message.SendAttemptCount == message.MaxSendAttempts, SendAttemptCount: message.SendAttemptCount, @@ -717,6 +985,32 @@ func (service *MessageService) CheckExpired(ctx context.Context, params MessageC return nil } +// MessageSearchParams are parameters for searching messages +type MessageSearchParams struct { + repositories.IndexParams + UserID entities.UserID + Owners []string + Types []entities.MessageType + Statuses []entities.MessageStatus +} + +// SearchMessages fetches all the messages for a user +func (service *MessageService) SearchMessages(ctx context.Context, params *MessageSearchParams) ([]*entities.Message, error) { + ctx, span := service.tracer.Start(ctx) + defer span.End() + + ctxLogger := service.tracer.CtxLogger(service.logger, span) + + messages, err := service.repository.Search(ctx, params.UserID, params.Owners, params.Types, params.Statuses, params.IndexParams) + if err != nil { + msg := fmt.Sprintf("could not search messages with parms [%+#v]", params) + return nil, service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("fetched [%d] messages with prams [%+#v]", len(messages), params)) + return messages, nil +} + func (service *MessageService) phoneSettings(ctx context.Context, userID entities.UserID, owner string) (uint, entities.SIM) { ctx, span := service.tracer.Start(ctx) defer span.End() @@ -751,8 +1045,10 @@ func (service *MessageService) storeSentMessage(ctx context.Context, payload eve Contact: payload.Contact, UserID: payload.UserID, Content: payload.Content, + Attachments: payload.Attachments, RequestID: payload.RequestID, SIM: payload.SIM, + Encrypted: payload.Encrypted, ScheduledSendTime: payload.ScheduledSendTime, Type: entities.MessageTypeMobileTerminated, Status: entities.MessageStatusPending, @@ -772,6 +1068,43 @@ func (service *MessageService) storeSentMessage(ctx context.Context, payload eve return message, nil } +// storeMissedCallMessage a new message +func (service *MessageService) storeMissedCallMessage(ctx context.Context, payload *events.MessageCallMissedPayload) (*entities.Message, error) { + ctx, span := service.tracer.Start(ctx) + defer span.End() + + ctxLogger := service.tracer.CtxLogger(service.logger, span) + + message := &entities.Message{ + ID: payload.MessageID, + Owner: payload.Owner, + Contact: payload.Contact, + UserID: payload.UserID, + SIM: payload.SIM, + Type: entities.MessageTypeCallMissed, + Status: entities.MessageStatusReceived, + RequestReceivedAt: payload.Timestamp, + CreatedAt: time.Now().UTC(), + UpdatedAt: time.Now().UTC(), + OrderTimestamp: payload.Timestamp, + } + + if err := service.repository.Store(ctx, message); err != nil { + msg := fmt.Sprintf("cannot save missed call message with id [%s]", payload.MessageID) + return nil, service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("missed call message saved with id [%s]", payload.MessageID)) + return message, nil +} + +func (service *MessageService) enrichErrorMessage(message string) string { + if strings.Contains(message, "android.permission.SEND_SMS") { + return message + " You need to grant the SMS permission to the httpSMS Android app https://httpsms.com/blog/grant-send-and-read-sms-permissions-on-android" + } + return message +} + func (service *MessageService) createMessageSendExpiredEvent(source string, payload events.MessageSendExpiredPayload) (cloudevents.Event, error) { return service.createEvent(events.EventTypeMessageSendExpired, source, payload) } diff --git a/api/pkg/services/message_thread_service.go b/api/pkg/services/message_thread_service.go index 2556e09e..d2027506 100644 --- a/api/pkg/services/message_thread_service.go +++ b/api/pkg/services/message_thread_service.go @@ -6,6 +6,8 @@ import ( "math/rand" "time" + "github.com/NdoleStudio/httpsms/pkg/events" + "github.com/NdoleStudio/httpsms/pkg/entities" "github.com/NdoleStudio/httpsms/pkg/repositories" "github.com/NdoleStudio/httpsms/pkg/telemetry" @@ -15,9 +17,11 @@ import ( // MessageThreadService is handles message requests type MessageThreadService struct { - logger telemetry.Logger - tracer telemetry.Tracer - repository repositories.MessageThreadRepository + service + logger telemetry.Logger + tracer telemetry.Tracer + repository repositories.MessageThreadRepository + eventDispatcher *EventDispatcher } // NewMessageThreadService creates a new MessageThreadService @@ -25,11 +29,13 @@ func NewMessageThreadService( logger telemetry.Logger, tracer telemetry.Tracer, repository repositories.MessageThreadRepository, + eventDispatcher *EventDispatcher, ) (s *MessageThreadService) { return &MessageThreadService{ - logger: logger.WithService(fmt.Sprintf("%T", s)), - tracer: tracer, - repository: repository, + logger: logger.WithService(fmt.Sprintf("%T", s)), + tracer: tracer, + eventDispatcher: eventDispatcher, + repository: repository, } } @@ -44,6 +50,20 @@ type MessageThreadUpdateParams struct { Timestamp time.Time } +// DeleteAllForUser deletes all entities.MessageThread for an entities.UserID. +func (service *MessageThreadService) DeleteAllForUser(ctx context.Context, userID entities.UserID) error { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + if err := service.repository.DeleteAllForUser(ctx, userID); err != nil { + msg := fmt.Sprintf("could not delete [entities.MessageThread] for user with ID [%s]", userID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("deleted all [entities.MessageThread] for user with ID [%s]", userID)) + return nil +} + // UpdateThread updates a thread between 2 parties when a timestamp changes func (service *MessageThreadService) UpdateThread(ctx context.Context, params MessageThreadUpdateParams) error { ctx, span := service.tracer.Start(ctx) @@ -62,12 +82,12 @@ func (service *MessageThreadService) UpdateThread(ctx context.Context, params Me return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) } - if thread.OrderTimestamp.Unix() > params.Timestamp.Unix() && thread.Status != entities.MessageStatusSending && thread.LastMessageID == params.MessageID { + if thread.OrderTimestamp.Unix() > params.Timestamp.Unix() && thread.Status != entities.MessageStatusSending && thread.HasLastMessage(params.MessageID) { ctxLogger.Warn(stacktrace.NewError(fmt.Sprintf("thread [%s] has timestamp [%s] and status [%s] which is greater than timestamp [%s] for message [%s] and status [%s]", thread.ID, thread.OrderTimestamp, thread.Status, params.Timestamp, params.MessageID, params.Status))) return nil } - if thread.Status == entities.MessageStatusDelivered && thread.LastMessageID == params.MessageID { + if thread.Status == entities.MessageStatusDelivered && thread.LastMessageID != nil && thread.HasLastMessage(params.MessageID) { ctxLogger.Warn(stacktrace.NewError(fmt.Sprintf("thread [%s] already has status [%s] not updating with status [%s] for message [%s]", thread.ID, thread.Status, params.Status, params.MessageID))) return nil } @@ -110,6 +130,48 @@ func (service *MessageThreadService) UpdateStatus(ctx context.Context, params Me return thread, nil } +// UpdateAfterDeletedMessage updates a thread after the last message has been deleted +func (service *MessageThreadService) UpdateAfterDeletedMessage(ctx context.Context, payload *events.MessageAPIDeletedPayload) error { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + thread, err := service.repository.LoadByOwnerContact(ctx, payload.UserID, payload.Owner, payload.Contact) + if err != nil { + msg := fmt.Sprintf("cannot find thread for user [%s] with owner [%s], and contact [%s]", payload.UserID, payload.Owner, payload.Contact) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if payload.PreviousMessageID == nil { + if err = service.repository.Delete(ctx, thread.UserID, thread.ID); err != nil { + msg := fmt.Sprintf("cannot delete thread with ID [%s] for user [%s] and owner [%s]", thread.ID, thread.UserID, thread.Owner) + ctxLogger.Error(stacktrace.Propagate(err, msg)) + return nil + } + msg := fmt.Sprintf("previous message ID is nil for thread with ID [%s] and user [%s]", thread.ID, thread.UserID) + ctxLogger.Info(msg) + return nil + } + + if thread.LastMessageID != nil && *thread.LastMessageID != payload.MessageID { + msg := fmt.Sprintf("last message ID [%s] does not match message ID [%s] for thread with ID [%s]", *thread.LastMessageID, payload.MessageID, thread.ID) + ctxLogger.Info(msg) + return nil + } + + thread.LastMessageContent = payload.PreviousMessageContent + thread.LastMessageID = payload.PreviousMessageID + thread.Status = *payload.PreviousMessageStatus + thread.UpdatedAt = time.Now().UTC() + + if err = service.repository.Update(ctx, thread); err != nil { + msg := fmt.Sprintf("cannot update thread with ID [%s] for user with ID [%s]", thread.ID, thread.UserID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("last message has been removed from thread with ID [%s] and userID [%s]", thread.ID, thread.UserID)) + return nil +} + func (service *MessageThreadService) createThread(ctx context.Context, params MessageThreadUpdateParams) error { ctx, span := service.tracer.Start(ctx) defer span.End() @@ -123,9 +185,9 @@ func (service *MessageThreadService) createThread(ctx context.Context, params Me UserID: params.UserID, IsArchived: false, Color: service.getColor(), - LastMessageContent: params.Content, + LastMessageContent: ¶ms.Content, Status: params.Status, - LastMessageID: params.MessageID, + LastMessageID: ¶ms.MessageID, CreatedAt: time.Now().UTC(), UpdatedAt: time.Now().UTC(), OrderTimestamp: params.Timestamp, @@ -195,3 +257,52 @@ func (service *MessageThreadService) GetThreads(ctx context.Context, params Mess ctxLogger.Info(fmt.Sprintf("fetched [%d] threads with params [%+#v]", len(*threads), params)) return threads, nil } + +// GetThread fetches an entities.MessageThread message thread by the ID +func (service *MessageThreadService) GetThread(ctx context.Context, userID entities.UserID, messageThreadID uuid.UUID) (*entities.MessageThread, error) { + ctx, span := service.tracer.Start(ctx) + defer span.End() + + thread, err := service.repository.Load(ctx, userID, messageThreadID) + if err != nil { + msg := fmt.Sprintf("could not fetch thread with ID [%s] for user [%s]", messageThreadID, userID) + return nil, service.tracer.WrapErrorSpan(span, stacktrace.PropagateWithCode(err, stacktrace.GetCode(err), msg)) + } + + return thread, nil +} + +// DeleteThread deletes an entities.MessageThread from the database +func (service *MessageThreadService) DeleteThread(ctx context.Context, source string, thread *entities.MessageThread) error { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + if err := service.repository.Delete(ctx, thread.UserID, thread.ID); err != nil { + msg := fmt.Sprintf("could not delete message thread with ID [%s] for user with ID [%s]", thread.ID, thread.UserID) + return service.tracer.WrapErrorSpan(span, stacktrace.PropagateWithCode(err, stacktrace.GetCode(err), msg)) + } + + event, err := service.createEvent(events.MessageThreadAPIDeleted, source, &events.MessageThreadAPIDeletedPayload{ + MessageThreadID: thread.ID, + UserID: thread.UserID, + Owner: thread.Owner, + Contact: thread.Contact, + IsArchived: thread.IsArchived, + Color: thread.Color, + Status: thread.Status, + Timestamp: time.Now().UTC(), + }) + if err != nil { + msg := fmt.Sprintf("cannot create [%T] for message thread dleted with ID [%s]", event, thread.ID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("created event [%s] with id [%s] for message thread [%s]", event.Type(), event.ID(), thread.ID)) + if err = service.eventDispatcher.Dispatch(ctx, event); err != nil { + msg := fmt.Sprintf("cannot dispatch event [%s] with id [%s] for message thread [%s]", event.Type(), event.ID(), thread.ID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("dispatched [%s] event with id [%s] for message thread [%s]", event.Type(), event.ID(), thread.ID)) + return nil +} diff --git a/api/pkg/services/phone_api_key_service.go b/api/pkg/services/phone_api_key_service.go new file mode 100644 index 00000000..5a148583 --- /dev/null +++ b/api/pkg/services/phone_api_key_service.go @@ -0,0 +1,202 @@ +package services + +import ( + "context" + "crypto/rand" + "encoding/base64" + "fmt" + "time" + + "github.com/lib/pq" + + "github.com/NdoleStudio/httpsms/pkg/entities" + "github.com/NdoleStudio/httpsms/pkg/repositories" + "github.com/NdoleStudio/httpsms/pkg/telemetry" + "github.com/google/uuid" + "github.com/palantir/stacktrace" +) + +// PhoneAPIKeyService is responsible for managing entities.PhoneAPIKey +type PhoneAPIKeyService struct { + service + logger telemetry.Logger + tracer telemetry.Tracer + phoneRepository repositories.PhoneRepository + repository repositories.PhoneAPIKeyRepository +} + +// NewPhoneAPIKeyService creates a new PhoneAPIKeyService +func NewPhoneAPIKeyService( + logger telemetry.Logger, + tracer telemetry.Tracer, + phoneRepository repositories.PhoneRepository, + repository repositories.PhoneAPIKeyRepository, +) *PhoneAPIKeyService { + return &PhoneAPIKeyService{ + logger: logger.WithService(fmt.Sprintf("%T", &PhoneAPIKeyService{})), + tracer: tracer, + phoneRepository: phoneRepository, + repository: repository, + } +} + +// Index fetches the entities.Webhook for an entities.UserID +func (service *PhoneAPIKeyService) Index(ctx context.Context, userID entities.UserID, params repositories.IndexParams) ([]*entities.PhoneAPIKey, error) { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + phoneAPIKeys, err := service.repository.Index(ctx, userID, params) + if err != nil { + msg := fmt.Sprintf("could not fetch phone API Keys with params [%+#v]", params) + return nil, service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("fetched [%d] phone API Keys with prams [%+#v]", len(phoneAPIKeys), params)) + return phoneAPIKeys, nil +} + +// Create a new entities.PhoneAPIKey +func (service *PhoneAPIKeyService) Create(ctx context.Context, authContext entities.AuthContext, name string) (*entities.PhoneAPIKey, error) { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + apiKey, err := service.generateAPIKey(64) + if err != nil { + return nil, stacktrace.Propagate(err, "cannot generate API key") + } + + phoneAPIKey := &entities.PhoneAPIKey{ + ID: uuid.New(), + Name: name, + UserID: authContext.ID, + UserEmail: authContext.Email, + PhoneNumbers: pq.StringArray{}, + PhoneIDs: pq.StringArray{}, + APIKey: "pk_" + apiKey, + CreatedAt: time.Now().UTC(), + UpdatedAt: time.Now().UTC(), + } + + if err = service.repository.Create(ctx, phoneAPIKey); err != nil { + msg := fmt.Sprintf("cannot create PhoneAPIKey for user [%s]", authContext.ID) + return nil, service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("created [%T] with ID [%s] for user ID [%s]", phoneAPIKey, phoneAPIKey.ID, authContext.ID)) + return phoneAPIKey, nil +} + +// Delete an entities.PhoneAPIKey +func (service *PhoneAPIKeyService) Delete(ctx context.Context, userID entities.UserID, phoneAPIKeyID uuid.UUID) error { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + phoneAPIKey, err := service.repository.Load(ctx, userID, phoneAPIKeyID) + if err != nil { + msg := fmt.Sprintf("cannot load [%T] with ID [%s] for user [%s]", &entities.PhoneAPIKey{}, phoneAPIKeyID, userID.String()) + return stacktrace.Propagate(err, msg) + } + + if err = service.repository.Delete(ctx, phoneAPIKey); err != nil { + msg := fmt.Sprintf("cannot delete [%T] with ID [%s] for user [%s]", phoneAPIKey, phoneAPIKey.ID, phoneAPIKey.UserID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("deleted [%T] with ID [%s] for user ID [%s]", phoneAPIKey, phoneAPIKey.ID, userID)) + return nil +} + +// RemovePhone removes the phone from the phone API key +func (service *PhoneAPIKeyService) RemovePhone(ctx context.Context, userID entities.UserID, phoneAPIKeyID uuid.UUID, phoneID uuid.UUID) error { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + phone, err := service.phoneRepository.LoadByID(ctx, userID, phoneID) + if err != nil { + msg := fmt.Sprintf("cannot load [%T] with ID [%s] for user [%s]", &entities.Phone{}, phoneID, userID.String()) + return stacktrace.Propagate(err, msg) + } + + phoneAPIKey, err := service.repository.Load(ctx, userID, phoneAPIKeyID) + if err != nil { + msg := fmt.Sprintf("cannot load [%T] with ID [%s] for user [%s]", &entities.PhoneAPIKey{}, phoneAPIKeyID, userID.String()) + return stacktrace.Propagate(err, msg) + } + + if err = service.repository.RemovePhone(ctx, phoneAPIKey, phone); err != nil { + msg := fmt.Sprintf("cannot remove [%T] with ID [%s] from phone API key with ID [%s] for user [%s]", phone, phone.ID, phoneAPIKey.ID, userID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("removed [%T] with ID [%s] from [%T] with ID [%s] for user ID [%s]", phone, phoneID, phoneAPIKey, phoneAPIKeyID, userID)) + return nil +} + +// RemovePhoneByID removes the phone from the phone API key by phone number and phoneID +func (service *PhoneAPIKeyService) RemovePhoneByID(ctx context.Context, userID entities.UserID, phoneID uuid.UUID, phoneNumber string) error { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + if err := service.repository.RemovePhoneByID(ctx, userID, phoneID, phoneNumber); err != nil { + msg := fmt.Sprintf("cannot remove [%T] with ID [%s] and number [%s] for user [%s]", &entities.Phone{}, phoneID, phoneNumber, userID.String()) + return stacktrace.Propagate(err, msg) + } + + ctxLogger.Info(fmt.Sprintf("removed phone with ID [%s] from [%T] for user ID [%s]", phoneID, &entities.PhoneAPIKey{}, userID)) + return nil +} + +// DeleteAllForUser removes all entities.PhoneAPIKey for a user +func (service *PhoneAPIKeyService) DeleteAllForUser(ctx context.Context, userID entities.UserID) error { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + if err := service.repository.DeleteAllForUser(ctx, userID); err != nil { + msg := fmt.Sprintf("cannot delete all [%T] for user ID [%s]", &entities.PhoneAPIKey{}, userID) + return stacktrace.Propagate(err, msg) + } + + ctxLogger.Info(fmt.Sprintf("deleted all [%T] for user ID [%s]", &entities.PhoneAPIKey{}, userID)) + return nil +} + +// AddPhone adds a phone to the phone API key +func (service *PhoneAPIKeyService) AddPhone(ctx context.Context, userID entities.UserID, phoneAPIKeyID uuid.UUID, phoneID uuid.UUID) error { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + phone, err := service.phoneRepository.LoadByID(ctx, userID, phoneID) + if err != nil { + msg := fmt.Sprintf("cannot load [%T] with ID [%s] for user [%s]", &entities.Phone{}, phoneID, userID.String()) + return stacktrace.Propagate(err, msg) + } + + phoneAPIKey, err := service.repository.Load(ctx, userID, phoneAPIKeyID) + if err != nil { + msg := fmt.Sprintf("cannot load [%T] with ID [%s] for user [%s]", &entities.PhoneAPIKey{}, phoneAPIKeyID, userID.String()) + return stacktrace.Propagate(err, msg) + } + + if err = service.repository.AddPhone(ctx, phoneAPIKey, phone); err != nil { + msg := fmt.Sprintf("cannot add [%T] with ID [%s] to phone API key with ID [%s] for user [%s]", phone, phone.ID, phoneAPIKey.ID, userID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("added [%T] with ID [%s] to [%T] with ID [%s] for user ID [%s]", phone, phone.ID, phoneAPIKey, phoneAPIKeyID, userID)) + return nil +} + +func (service *PhoneAPIKeyService) generateRandomBytes(n int) ([]byte, error) { + b := make([]byte, n) + // Note that err == nil only if we read len(b) bytes. + if _, err := rand.Read(b); err != nil { + return nil, stacktrace.Propagate(err, fmt.Sprintf("cannot generate [%d] random bytes", n)) + } + + return b, nil +} + +func (service *PhoneAPIKeyService) generateAPIKey(n int) (string, error) { + b, err := service.generateRandomBytes(n) + return base64.URLEncoding.EncodeToString(b)[0:n], stacktrace.Propagate(err, fmt.Sprintf("cannot generate [%d] random bytes", n)) +} diff --git a/api/pkg/services/phone_notification_service.go b/api/pkg/services/phone_notification_service.go index 480f6c5d..7907d6d6 100644 --- a/api/pkg/services/phone_notification_service.go +++ b/api/pkg/services/phone_notification_service.go @@ -47,6 +47,20 @@ func NewNotificationService( } } +// DeleteAllForUser deletes all entities.PhoneNotification for an entities.UserID. +func (service *PhoneNotificationService) DeleteAllForUser(ctx context.Context, userID entities.UserID) error { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + if err := service.phoneNotificationRepository.DeleteAllForUser(ctx, userID); err != nil { + msg := fmt.Sprintf("could not delete all [entities.PhoneNotification] for user with ID [%s]", userID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("deleted all [entities.PhoneNotification] for user with ID [%s]", userID)) + return nil +} + // SendHeartbeatFCM sends a heartbeat message so the phone can request a heartbeat func (service *PhoneNotificationService) SendHeartbeatFCM(ctx context.Context, payload *events.PhoneHeartbeatMissedPayload) error { ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) @@ -94,7 +108,7 @@ type PhoneNotificationSendParams struct { // Send sends a message when a message is sent func (service *PhoneNotificationService) Send(ctx context.Context, params *PhoneNotificationSendParams) error { - ctx, span := service.tracer.Start(ctx) + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) defer span.End() phone, err := service.phoneRepository.LoadByID(ctx, params.UserID, params.PhoneID) @@ -120,8 +134,11 @@ func (service *PhoneNotificationService) Send(ctx context.Context, params *Phone Token: *phone.FcmToken, }) if err != nil { - return service.handleNotificationFailed(ctx, err, params) + ctxLogger.Warn(stacktrace.Propagate(err, fmt.Sprintf("cannot send FCM to phone with ID [%s] for user with ID [%s] and message [%s]", phone.ID, phone.UserID, params.MessageID))) + msg := fmt.Sprintf("cannot send notification for to your phone [%s]. Reinstall the httpSMS app on your Android phone.", phone.PhoneNumber) + return service.handleNotificationFailed(ctx, errors.New(msg), params) } + return service.handleNotificationSent(ctx, phone, result, params) } @@ -130,6 +147,7 @@ type PhoneNotificationScheduleParams struct { UserID entities.UserID Owner string Source string + Encrypted bool Contact string Content string SIM entities.SIM @@ -200,6 +218,7 @@ func (service *PhoneNotificationService) dispatchMessageNotificationScheduled(ct MessageID: notification.MessageID, Owner: params.Owner, Contact: params.Contact, + Encrypted: params.Encrypted, Content: params.Content, SIM: params.SIM, UserID: notification.UserID, diff --git a/api/pkg/services/phone_service.go b/api/pkg/services/phone_service.go index dba82278..df8e2104 100644 --- a/api/pkg/services/phone_service.go +++ b/api/pkg/services/phone_service.go @@ -42,8 +42,22 @@ func NewPhoneService( } } +// DeleteAllForUser deletes all entities.Phone for an entities.UserID. +func (service *PhoneService) DeleteAllForUser(ctx context.Context, userID entities.UserID) error { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + if err := service.repository.DeleteAllForUser(ctx, userID); err != nil { + msg := fmt.Sprintf("could not delete all [entities.Phone] for user with ID [%s]", userID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("deleted all [entities.Phone] for user with ID [%s]", userID)) + return nil +} + // Index fetches the heartbeats for a phone number -func (service *PhoneService) Index(ctx context.Context, authUser entities.AuthUser, params repositories.IndexParams) (*[]entities.Phone, error) { +func (service *PhoneService) Index(ctx context.Context, authUser entities.AuthContext, params repositories.IndexParams) (*[]entities.Phone, error) { ctx, span := service.tracer.Start(ctx) defer span.End() @@ -69,27 +83,35 @@ func (service *PhoneService) Load(ctx context.Context, userID entities.UserID, o // PhoneUpsertParams are parameters for creating a new entities.Phone type PhoneUpsertParams struct { - PhoneNumber phonenumbers.PhoneNumber + PhoneNumber *phonenumbers.PhoneNumber FcmToken *string MessagesPerMinute *uint MaxSendAttempts *uint WebhookURL *string MessageExpirationDuration *time.Duration + MissedCallAutoReply *string SIM entities.SIM Source string UserID entities.UserID } // Upsert a new entities.Phone -func (service *PhoneService) Upsert(ctx context.Context, params PhoneUpsertParams) (*entities.Phone, error) { +func (service *PhoneService) Upsert(ctx context.Context, params *PhoneUpsertParams) (*entities.Phone, error) { ctx, span := service.tracer.Start(ctx) defer span.End() ctxLogger := service.tracer.CtxLogger(service.logger, span) - phone, err := service.repository.Load(ctx, params.UserID, phonenumbers.Format(¶ms.PhoneNumber, phonenumbers.E164)) + phone, err := service.repository.Load(ctx, params.UserID, phonenumbers.Format(params.PhoneNumber, phonenumbers.E164)) if stacktrace.GetCode(err) == repositories.ErrCodeNotFound { - return service.createPhone(ctx, params) + return service.createPhone(ctx, &PhoneFCMTokenParams{ + Source: params.Source, + PhoneNumber: params.PhoneNumber, + PhoneAPIKeyID: nil, + UserID: params.UserID, + FcmToken: params.FcmToken, + SIM: params.SIM, + }) } if err != nil { @@ -103,19 +125,27 @@ func (service *PhoneService) Upsert(ctx context.Context, params PhoneUpsertParam } ctxLogger.Info(fmt.Sprintf("phone updated with id [%s] in the phone repository for user [%s]", phone.ID, phone.UserID)) - return phone, service.dispatchPhoneUpdatedEvent(ctx, params.Source, phone) + return phone, service.dispatchPhoneUpdatedEvent(ctx, phone, &PhoneFCMTokenParams{ + Source: params.Source, + PhoneNumber: params.PhoneNumber, + PhoneAPIKeyID: nil, + UserID: params.UserID, + FcmToken: params.FcmToken, + SIM: params.SIM, + }) } -func (service *PhoneService) dispatchPhoneUpdatedEvent(ctx context.Context, source string, phone *entities.Phone) error { +func (service *PhoneService) dispatchPhoneUpdatedEvent(ctx context.Context, phone *entities.Phone, input *PhoneFCMTokenParams) error { ctx, span := service.tracer.Start(ctx) defer span.End() - event, err := service.createPhoneUpdatedEvent(source, events.PhoneUpdatedPayload{ - PhoneID: phone.ID, - UserID: phone.UserID, - Timestamp: phone.UpdatedAt, - Owner: phone.PhoneNumber, - SIM: phone.SIM, + event, err := service.createPhoneUpdatedEvent(input.Source, events.PhoneUpdatedPayload{ + PhoneID: phone.ID, + UserID: phone.UserID, + Timestamp: phone.UpdatedAt, + PhoneAPIKeyID: input.PhoneAPIKeyID, + Owner: phone.PhoneNumber, + SIM: phone.SIM, }) if err != nil { msg := fmt.Sprintf("cannot create event when phone [%s] is updated for user [%s]", phone.ID, phone.UserID) @@ -169,7 +199,44 @@ func (service *PhoneService) Delete(ctx context.Context, source string, userID e return nil } -func (service *PhoneService) createPhone(ctx context.Context, params PhoneUpsertParams) (*entities.Phone, error) { +// PhoneFCMTokenParams are parameters for upserting an entities.Phone +type PhoneFCMTokenParams struct { + Source string + PhoneNumber *phonenumbers.PhoneNumber + PhoneAPIKeyID *uuid.UUID + UserID entities.UserID + FcmToken *string + SIM entities.SIM +} + +// UpsertFCMToken the FCM token for an entities.Phone +func (service *PhoneService) UpsertFCMToken(ctx context.Context, params *PhoneFCMTokenParams) (*entities.Phone, error) { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + phone, err := service.repository.Load(ctx, params.UserID, phonenumbers.Format(params.PhoneNumber, phonenumbers.E164)) + if stacktrace.GetCode(err) == repositories.ErrCodeNotFound { + return service.createPhone(ctx, params) + } + + if err != nil { + msg := fmt.Sprintf("cannot upsert FCM token for user with id [%s] and number [%s]", params.UserID, params.PhoneNumber) + return nil, service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + phone.FcmToken = params.FcmToken + phone.SIM = params.SIM + + if err = service.repository.Save(ctx, phone); err != nil { + msg := fmt.Sprintf("cannot update phone with id [%s] and number [%s]", phone.ID, phone.PhoneNumber) + return nil, service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("phone updated with id [%s] in the phone repository for user [%s]", phone.ID, phone.UserID)) + return phone, service.dispatchPhoneUpdatedEvent(ctx, phone, params) +} + +func (service *PhoneService) createPhone(ctx context.Context, params *PhoneFCMTokenParams) (*entities.Phone, error) { ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) defer span.End() @@ -183,7 +250,8 @@ func (service *PhoneService) createPhone(ctx context.Context, params PhoneUpsert MessageExpirationSeconds: 10 * 60, // 10 minutes MaxSendAttempts: 2, SIM: params.SIM, - PhoneNumber: phonenumbers.Format(¶ms.PhoneNumber, phonenumbers.E164), + MissedCallAutoReply: nil, + PhoneNumber: phonenumbers.Format(params.PhoneNumber, phonenumbers.E164), CreatedAt: time.Now().UTC(), UpdatedAt: time.Now().UTC(), } @@ -194,7 +262,7 @@ func (service *PhoneService) createPhone(ctx context.Context, params PhoneUpsert } ctxLogger.Info(fmt.Sprintf("phone updated with id [%s] in the phone repository for user [%s]", phone.ID, phone.UserID)) - return phone, service.dispatchPhoneUpdatedEvent(ctx, params.Source, phone) + return phone, service.dispatchPhoneUpdatedEvent(ctx, phone, params) } func (service *PhoneService) createPhoneUpdatedEvent(source string, payload events.PhoneUpdatedPayload) (cloudevents.Event, error) { @@ -205,7 +273,7 @@ func (service *PhoneService) createPhoneDeletedEvent(source string, payload even return service.createEvent(events.EventTypePhoneDeleted, source, payload) } -func (service *PhoneService) update(phone *entities.Phone, params PhoneUpsertParams) *entities.Phone { +func (service *PhoneService) update(phone *entities.Phone, params *PhoneUpsertParams) *entities.Phone { if phone.FcmToken != nil { phone.FcmToken = params.FcmToken } @@ -221,6 +289,10 @@ func (service *PhoneService) update(phone *entities.Phone, params PhoneUpsertPar phone.MessageExpirationSeconds = uint(params.MessageExpirationDuration.Seconds()) } + if params.MissedCallAutoReply != nil { + phone.MissedCallAutoReply = params.MissedCallAutoReply + } + phone.SIM = params.SIM return phone diff --git a/api/pkg/services/user_service.go b/api/pkg/services/user_service.go index 60a59507..20c924b4 100644 --- a/api/pkg/services/user_service.go +++ b/api/pkg/services/user_service.go @@ -3,12 +3,14 @@ package services import ( "context" "fmt" + "io" + "net/http" "time" - "github.com/NdoleStudio/httpsms/pkg/events" - + "firebase.google.com/go/auth" "github.com/NdoleStudio/httpsms/pkg/emails" - lemonsqueezy "github.com/NdoleStudio/lemonsqueezy-go" + "github.com/NdoleStudio/httpsms/pkg/events" + "github.com/NdoleStudio/lemonsqueezy-go" "github.com/NdoleStudio/httpsms/pkg/repositories" "github.com/google/uuid" @@ -26,8 +28,10 @@ type UserService struct { emailFactory emails.UserEmailFactory mailer emails.Mailer repository repositories.UserRepository - marketingService *MarketingService + dispatcher *EventDispatcher + authClient *auth.Client lemonsqueezyClient *lemonsqueezy.Client + httpClient *http.Client } // NewUserService creates a new UserService @@ -37,22 +41,98 @@ func NewUserService( repository repositories.UserRepository, mailer emails.Mailer, emailFactory emails.UserEmailFactory, - marketingService *MarketingService, lemonsqueezyClient *lemonsqueezy.Client, + dispatcher *EventDispatcher, + authClient *auth.Client, + httpClient *http.Client, ) (s *UserService) { return &UserService{ logger: logger.WithService(fmt.Sprintf("%T", s)), tracer: tracer, mailer: mailer, - marketingService: marketingService, emailFactory: emailFactory, repository: repository, + dispatcher: dispatcher, + authClient: authClient, lemonsqueezyClient: lemonsqueezyClient, + httpClient: httpClient, + } +} + +// GetSubscriptionPayments fetches the subscription payments for an entities.User +func (service *UserService) GetSubscriptionPayments(ctx context.Context, userID entities.UserID) (invoices []lemonsqueezy.ApiResponseData[lemonsqueezy.SubscriptionInvoiceAttributes, lemonsqueezy.APIResponseRelationshipsSubscriptionInvoice], err error) { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + user, err := service.repository.Load(ctx, userID) + if err != nil { + msg := fmt.Sprintf("could not get [%T] with with ID [%s]", user, userID) + return invoices, service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if user.SubscriptionID == nil { + ctxLogger.Info(fmt.Sprintf("no subscription ID found for [%T] with ID [%s], returning empty invoices", user, user.ID)) + return invoices, nil + } + + ctxLogger.Info(fmt.Sprintf("fetching subscription payments for [%T] with ID [%s] and subscription [%s]", user, user.ID, *user.SubscriptionID)) + invoicesResponse, _, err := service.lemonsqueezyClient.SubscriptionInvoices.List(ctx, map[string]string{"filter[subscription_id]": *user.SubscriptionID}) + if err != nil { + msg := fmt.Sprintf("could not get invoices for subscription [%s] for [%T] with with ID [%s]", *user.SubscriptionID, user, user.ID) + return invoices, service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) } + + ctxLogger.Info(fmt.Sprintf("fetched [%d] payments for [%T] with ID [%s] and subscription ID [%s]", len(invoicesResponse.Data), user, user.ID, *user.SubscriptionID)) + return invoicesResponse.Data, nil +} + +// UserInvoiceGenerateParams are parameters for generating a subscription payment invoice +type UserInvoiceGenerateParams struct { + UserID entities.UserID + SubscriptionInvoiceID string + Name string + Address string + City string + State string + Country string + ZipCode string + Notes string +} + +// GenerateReceipt generates a receipt for a subscription payment. +func (service *UserService) GenerateReceipt(ctx context.Context, params *UserInvoiceGenerateParams) (io.Reader, error) { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + payload := map[string]string{ + "name": params.Name, + "address": params.Address, + "city": params.City, + "state": params.State, + "country": params.Country, + "zip_code": params.ZipCode, + "notes": params.Notes, + "locale": "en", + } + + invoice, _, err := service.lemonsqueezyClient.SubscriptionInvoices.Generate(ctx, params.SubscriptionInvoiceID, payload) + if err != nil { + msg := fmt.Sprintf("could not generate subscription payment invoice user with ID [%s] and subscription invoice ID [%s]", params.UserID, params.SubscriptionInvoiceID) + return nil, service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + response, err := service.httpClient.Get(invoice.Meta.Urls.DownloadInvoice) + if err != nil { + msg := fmt.Sprintf("could not download subscription payment invoice for user with ID [%s] and subscription invoice ID [%s]", params.UserID, params.SubscriptionInvoiceID) + return nil, service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("generated subscription payment invoice for user with ID [%s] and subscription invoice ID [%s]", params.UserID, params.SubscriptionInvoiceID)) + return response.Body, nil } // Get fetches or creates an entities.User -func (service *UserService) Get(ctx context.Context, authUser entities.AuthUser) (*entities.User, error) { +func (service *UserService) Get(ctx context.Context, source string, authUser entities.AuthContext) (*entities.User, error) { ctx, span := service.tracer.Start(ctx) defer span.End() @@ -63,12 +143,33 @@ func (service *UserService) Get(ctx context.Context, authUser entities.AuthUser) } if isNew { - service.marketingService.AddToList(ctx, user) + service.dispatchUserCreatedEvent(ctx, source, user) } return user, nil } +func (service *UserService) dispatchUserCreatedEvent(ctx context.Context, source string, user *entities.User) { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + event, err := service.createEvent(events.UserAccountCreated, source, &events.UserAccountCreatedPayload{ + UserID: user.ID, + Timestamp: time.Now().UTC(), + }) + if err != nil { + msg := fmt.Sprintf("cannot create event [%s] for user [%s]", events.UserAccountCreated, user.ID) + ctxLogger.Error(stacktrace.Propagate(err, msg)) + return + } + + if err = service.dispatcher.Dispatch(ctx, event); err != nil { + msg := fmt.Sprintf("cannot dispatch [%s] event for user [%s]", event.Type(), user.ID) + ctxLogger.Error(stacktrace.Propagate(err, msg)) + return + } +} + // GetByID fetches an entities.User func (service *UserService) GetByID(ctx context.Context, userID entities.UserID) (*entities.User, error) { ctx, span, _ := service.tracer.StartWithLogger(ctx, service.logger) @@ -86,11 +187,11 @@ func (service *UserService) GetByID(ctx context.Context, userID entities.UserID) // UserUpdateParams are parameters for updating an entities.User type UserUpdateParams struct { Timezone *time.Location - ActivePhoneID uuid.UUID + ActivePhoneID *uuid.UUID } // Update an entities.User -func (service *UserService) Update(ctx context.Context, authUser entities.AuthUser, params UserUpdateParams) (*entities.User, error) { +func (service *UserService) Update(ctx context.Context, source string, authUser entities.AuthContext, params UserUpdateParams) (*entities.User, error) { ctx, span := service.tracer.Start(ctx) defer span.End() @@ -103,11 +204,11 @@ func (service *UserService) Update(ctx context.Context, authUser entities.AuthUs } if isNew { - service.marketingService.AddToList(ctx, user) + service.dispatchUserCreatedEvent(ctx, source, user) } user.Timezone = params.Timezone.String() - user.ActivePhoneID = ¶ms.ActivePhoneID + user.ActivePhoneID = params.ActivePhoneID if err = service.repository.Update(ctx, user); err != nil { msg := fmt.Sprintf("cannot save user with id [%s]", user.ID) @@ -123,6 +224,7 @@ type UserNotificationUpdateParams struct { MessageStatusEnabled bool WebhookEnabled bool HeartbeatEnabled bool + NewsletterEnabled bool } // UpdateNotificationSettings for an entities.User @@ -139,6 +241,7 @@ func (service *UserService) UpdateNotificationSettings(ctx context.Context, user user.NotificationWebhookEnabled = params.WebhookEnabled user.NotificationHeartbeatEnabled = params.HeartbeatEnabled user.NotificationMessageStatusEnabled = params.MessageStatusEnabled + user.NotificationNewsletterEnabled = params.NewsletterEnabled if err = service.repository.Update(ctx, user); err != nil { msg := fmt.Sprintf("cannot save user with id [%s] in [%T]", user.ID, service.repository) @@ -149,6 +252,103 @@ func (service *UserService) UpdateNotificationSettings(ctx context.Context, user return user, nil } +// RotateAPIKey for an entities.User +func (service *UserService) RotateAPIKey(ctx context.Context, source string, userID entities.UserID) (*entities.User, error) { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + user, err := service.repository.RotateAPIKey(ctx, userID) + if err != nil { + msg := fmt.Sprintf("could not rotate API key for [%T] with ID [%s]", user, userID) + return nil, service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("rotated the api key for [%T] with ID [%s] in the [%T]", user, user.ID, service.repository)) + + event, err := service.createEvent(events.UserAPIKeyRotated, source, &events.UserAPIKeyRotatedPayload{ + UserID: user.ID, + Email: user.Email, + Timestamp: time.Now().UTC(), + Timezone: user.Timezone, + }) + if err != nil { + msg := fmt.Sprintf("cannot create event [%s] for user [%s]", events.UserAPIKeyRotated, user.ID) + ctxLogger.Error(stacktrace.Propagate(err, msg)) + return user, nil + } + + if err = service.dispatcher.Dispatch(ctx, event); err != nil { + msg := fmt.Sprintf("cannot dispatch [%s] event for user [%s]", event.Type(), user.ID) + ctxLogger.Error(stacktrace.Propagate(err, msg)) + return user, nil + } + + return user, nil +} + +// Delete an entities.User +func (service *UserService) Delete(ctx context.Context, source string, userID entities.UserID) error { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + user, err := service.repository.Load(ctx, userID) + if err != nil { + msg := fmt.Sprintf("cannot load user with ID [%s] from the [%T]", userID, service.repository) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if !user.IsOnFreePlan() && user.SubscriptionRenewsAt != nil && user.SubscriptionRenewsAt.After(time.Now()) { + msg := fmt.Sprintf("cannot delete user with ID [%s] because they are have an active [%s] subscription which renews at [%s]", userID, user.SubscriptionName, user.SubscriptionRenewsAt) + return service.tracer.WrapErrorSpan(span, stacktrace.NewError(msg)) + } + + if err = service.repository.Delete(ctx, user); err != nil { + msg := fmt.Sprintf("could not delete user with ID [%s] from the [%T]", userID, service.repository) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("sucessfully deleted user with ID [%s] in the [%T]", userID, service.repository)) + + event, err := service.createEvent(events.UserAccountDeleted, source, &events.UserAccountDeletedPayload{ + UserID: userID, + UserEmail: user.Email, + Timestamp: time.Now().UTC(), + }) + if err != nil { + msg := fmt.Sprintf("cannot create event [%s] for user [%s]", events.UserAccountDeleted, userID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err = service.dispatcher.Dispatch(ctx, event); err != nil { + msg := fmt.Sprintf("cannot dispatch [%s] event for user [%s]", event.Type(), userID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + +// SendAPIKeyRotatedEmail sends an email to an entities.User when the API key is rotated +func (service *UserService) SendAPIKeyRotatedEmail(ctx context.Context, payload *events.UserAPIKeyRotatedPayload) error { + ctx, span := service.tracer.Start(ctx) + defer span.End() + + ctxLogger := service.tracer.CtxLogger(service.logger, span) + + email, err := service.emailFactory.APIKeyRotated(payload.Email, payload.Timestamp, payload.Timezone) + if err != nil { + msg := fmt.Sprintf("cannot create api key rotated email for user [%s]", payload.UserID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if err = service.mailer.Send(ctx, email); err != nil { + msg := fmt.Sprintf("canot create api key rotated email to user [%s]", payload.UserID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("api key rotated email sent successfully to [%s] with user ID [%s]", payload.Email, payload.UserID)) + return nil +} + // UserSendPhoneDeadEmailParams are parameters for notifying a user when a phone is dead type UserSendPhoneDeadEmailParams struct { UserID entities.UserID @@ -171,7 +371,7 @@ func (service *UserService) SendPhoneDeadEmail(ctx context.Context, params *User } if !user.NotificationHeartbeatEnabled { - ctxLogger.Info(fmt.Sprintf("[%s] email notifications disabled for user [%s] with owner [%s]", events.EventTypePhoneHeartbeatDead, params.UserID, params.Owner)) + ctxLogger.Info(fmt.Sprintf("[%s] email notifications disabled for user [%s] with owner [%s]", events.EventTypePhoneHeartbeatOffline, params.UserID, params.Owner)) return nil } @@ -252,7 +452,7 @@ func (service *UserService) GetSubscriptionUpdateURL(ctx context.Context, userID return url, service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) } - return subscription.Data.Attributes.Urls.UpdatePaymentMethod, nil + return subscription.Data.Attributes.Urls.CustomerPortal, nil } // CancelSubscription starts a subscription for an entities.User @@ -280,7 +480,7 @@ func (service *UserService) CancelSubscription(ctx context.Context, params *even return nil } -// ExpireSubscription starts a subscription for an entities.User +// ExpireSubscription finishes a subscription for an entities.User func (service *UserService) ExpireSubscription(ctx context.Context, params *events.UserSubscriptionExpiredPayload) error { ctx, span := service.tracer.Start(ctx) defer span.End() @@ -304,3 +504,48 @@ func (service *UserService) ExpireSubscription(ctx context.Context, params *even return nil } + +// UpdateSubscription updates a subscription for an entities.User +func (service *UserService) UpdateSubscription(ctx context.Context, params *events.UserSubscriptionUpdatedPayload) error { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + user, err := service.repository.Load(ctx, params.UserID) + if err != nil { + msg := fmt.Sprintf("could not get [%T] with with ID [%s]", user, params.UserID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + if params.SubscriptionStatus != "active" { + msg := fmt.Sprintf("subscription status is [%s] for [%T] with with ID [%s]", params.SubscriptionStatus, user, params.UserID) + ctxLogger.Info(msg) + return nil + } + + user.SubscriptionID = ¶ms.SubscriptionID + user.SubscriptionName = params.SubscriptionName + user.SubscriptionEndsAt = params.SubscriptionEndsAt + user.SubscriptionRenewsAt = ¶ms.SubscriptionRenewsAt + user.SubscriptionStatus = ¶ms.SubscriptionStatus + + if err = service.repository.Update(ctx, user); err != nil { + msg := fmt.Sprintf("could not update [%T] with with ID [%s] after subscription update", user, params.UserID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + return nil +} + +// DeleteAuthUser deletes an entities.AuthContext from firebase +func (service *UserService) DeleteAuthUser(ctx context.Context, userID entities.UserID) error { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + if err := service.authClient.DeleteUser(ctx, userID.String()); err != nil { + msg := fmt.Sprintf("could not delete [entities.AuthContext] from firebase with ID [%s]", userID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("deleted [entities.AuthContext] from firebase for user with ID [%s]", userID)) + return nil +} diff --git a/api/pkg/services/webhook_service.go b/api/pkg/services/webhook_service.go index e3b4f9c2..e4dd0a7b 100644 --- a/api/pkg/services/webhook_service.go +++ b/api/pkg/services/webhook_service.go @@ -11,6 +11,7 @@ import ( "sync" "time" + "github.com/avast/retry-go/v5" "github.com/pkg/errors" "github.com/gofiber/fiber/v2" @@ -21,7 +22,7 @@ import ( "github.com/NdoleStudio/httpsms/pkg/repositories" "github.com/NdoleStudio/httpsms/pkg/telemetry" cloudevents "github.com/cloudevents/sdk-go/v2" - "github.com/golang-jwt/jwt" + "github.com/golang-jwt/jwt/v5" "github.com/google/uuid" "github.com/lib/pq" "github.com/palantir/stacktrace" @@ -54,6 +55,20 @@ func NewWebhookService( } } +// DeleteAllForUser deletes all entities.Webhook for an entities.UserID. +func (service *WebhookService) DeleteAllForUser(ctx context.Context, userID entities.UserID) error { + ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) + defer span.End() + + if err := service.repository.DeleteAllForUser(ctx, userID); err != nil { + msg := fmt.Sprintf("could not delete all [entities.Webhook] for user with ID [%s]", userID) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("deleted all [entities.Webhook] for user with ID [%s]", userID)) + return nil +} + // Index fetches the entities.Webhook for an entities.UserID func (service *WebhookService) Index(ctx context.Context, userID entities.UserID, params repositories.IndexParams) ([]*entities.Webhook, error) { ctx, span := service.tracer.Start(ctx) @@ -67,7 +82,7 @@ func (service *WebhookService) Index(ctx context.Context, userID entities.UserID return nil, service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) } - ctxLogger.Info(fmt.Sprintf("fetched [%d] messages with prams [%+#v]", len(webhooks), params)) + ctxLogger.Info(fmt.Sprintf("fetched [%d] webhooks with prams [%+#v]", len(webhooks), params)) return webhooks, nil } @@ -124,7 +139,7 @@ func (service *WebhookService) Store(ctx context.Context, params *WebhookStorePa return nil, service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) } - ctxLogger.Info(fmt.Sprintf("webhook saved with id [%s] in the [%T]", webhook.ID, service.repository)) + ctxLogger.Info(fmt.Sprintf("webhook saved with id [%s] for user [%s] in the [%T]", webhook.ID, webhook.UserID, service.repository)) return webhook, nil } @@ -196,37 +211,52 @@ func (service *WebhookService) sendNotification(ctx context.Context, event cloud ctx, span, ctxLogger := service.tracer.StartWithLogger(ctx, service.logger) defer span.End() - requestCtx, cancel := context.WithTimeout(ctx, 5*time.Second) - defer cancel() + attempts := 0 + err := retry.New(retry.Attempts(2)).Do(func() error { + attempts++ - request, err := service.createRequest(requestCtx, event, webhook) - if err != nil { - msg := fmt.Sprintf("cannot create [%s] event to webhook [%s] for user [%s]", event.Type(), webhook.URL, webhook.UserID) - ctxLogger.Error(service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))) - return - } + requestCtx, cancel := context.WithTimeout(ctx, 5*time.Second) + defer cancel() - response, err := service.client.Do(request) - if err != nil { - ctxLogger.Warn(stacktrace.Propagate(err, fmt.Sprintf("cannot send [%s] event to webhook [%s] for user [%s]", event.Type(), webhook.URL, webhook.UserID))) - service.handleWebhookSendFailed(ctx, event, webhook, owner, err, nil) - return - } + request, err := service.createRequest(requestCtx, event, webhook) + if err != nil { + msg := fmt.Sprintf("cannot create [%s] event to webhook [%s] for user [%s] after [%d] attempts", event.Type(), webhook.URL, webhook.UserID, attempts) + return service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } - defer func() { - err = response.Body.Close() + response, err := service.client.Do(request) if err != nil { - ctxLogger.Error(stacktrace.Propagate(err, fmt.Sprintf("cannot close response body for [%s] event with ID [%s]", event.Type(), event.ID()))) + ctxLogger.Warn(stacktrace.Propagate(err, fmt.Sprintf("cannot send [%s] event to webhook [%s] for user [%s] after [%d] attempts", event.Type(), webhook.URL, webhook.UserID, attempts))) + if attempts == 1 { + return err + } + service.handleWebhookSendFailed(ctx, event, webhook, owner, err, response) + return nil } - }() - if response.StatusCode >= 400 { - ctxLogger.Info(fmt.Sprintf("cannot send [%s] event to webhook [%s] for user [%s] with response code [%d]", event.Type(), webhook.URL, webhook.UserID, response.StatusCode)) - service.handleWebhookSendFailed(ctx, event, webhook, owner, stacktrace.NewError(http.StatusText(response.StatusCode)), response) - return - } + defer func() { + err = response.Body.Close() + if err != nil { + ctxLogger.Error(stacktrace.Propagate(err, fmt.Sprintf("cannot close response body for [%s] event with ID [%s] after [%d] attempts", event.Type(), event.ID(), attempts))) + } + }() + + if response.StatusCode >= 400 { + ctxLogger.Info(fmt.Sprintf("cannot send [%s] event to webhook [%s] for user [%s] with response code [%d] after [%d] attempts", event.Type(), webhook.URL, webhook.UserID, response.StatusCode, attempts)) + if attempts == 1 { + return stacktrace.NewError(http.StatusText(response.StatusCode)) + } + service.handleWebhookSendFailed(ctx, event, webhook, owner, stacktrace.NewError(http.StatusText(response.StatusCode)), response) + return nil + } - ctxLogger.Info(fmt.Sprintf("sent webhook to url [%s] for event [%s] with ID [%s] and response code [%d]", webhook.URL, event.Type(), event.ID(), response.StatusCode)) + ctxLogger.Info(fmt.Sprintf("sent webhook to url [%s] for event [%s] with ID [%s] and response code [%d]", webhook.URL, event.Type(), event.ID(), response.StatusCode)) + return nil + }) + if err != nil { + msg := fmt.Sprintf("cannot handle [%s] event to webhook [%s] for user [%s] after [%d] attempts", event.Type(), webhook.URL, webhook.UserID, attempts) + ctxLogger.Error(service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))) + } } func (service *WebhookService) createRequest(ctx context.Context, event cloudevents.Event, webhook *entities.Webhook) (*http.Request, error) { @@ -309,12 +339,12 @@ func (service *WebhookService) getPayload(ctxLogger telemetry.Logger, event clou } func (service *WebhookService) getAuthToken(webhook *entities.Webhook) (string, error) { - token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{ - Audience: webhook.URL, - ExpiresAt: time.Now().UTC().Add(10 * time.Minute).Unix(), - IssuedAt: time.Now().UTC().Unix(), + token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.RegisteredClaims{ + Audience: []string{webhook.URL}, + ExpiresAt: jwt.NewNumericDate(time.Now().UTC().Add(10 * time.Minute)), + IssuedAt: jwt.NewNumericDate(time.Now().UTC()), Issuer: "api.httpsms.com", - NotBefore: time.Now().UTC().Add(-10 * time.Minute).Unix(), + NotBefore: jwt.NewNumericDate(time.Now().UTC().Add(-10 * time.Minute)), Subject: string(webhook.UserID), }) return token.SignedString([]byte(webhook.SigningKey)) @@ -331,12 +361,13 @@ func (service *WebhookService) handleWebhookSendFailed(ctx context.Context, even EventID: event.ID(), Owner: owner, EventType: event.Type(), + EventPayload: string(event.Data()), HTTPResponseStatusCode: nil, ErrorMessage: err.Error(), } if errors.Is(err, context.DeadlineExceeded) { - payload.ErrorMessage = "TIMOUT after 5 seconds" + payload.ErrorMessage = "TIMOUT after 10 seconds" } if response != nil { diff --git a/api/pkg/validators/bulk_message_handler_validator.go b/api/pkg/validators/bulk_message_handler_validator.go index b607287f..3fa0c35c 100644 --- a/api/pkg/validators/bulk_message_handler_validator.go +++ b/api/pkg/validators/bulk_message_handler_validator.go @@ -12,6 +12,7 @@ import ( "github.com/xuri/excelize/v2" + "github.com/NdoleStudio/httpsms/pkg/cache" "github.com/NdoleStudio/httpsms/pkg/entities" "github.com/NdoleStudio/httpsms/pkg/repositories" "github.com/NdoleStudio/httpsms/pkg/requests" @@ -30,6 +31,7 @@ type BulkMessageHandlerValidator struct { userService *services.UserService logger telemetry.Logger tracer telemetry.Tracer + cache cache.Cache } // NewBulkMessageHandlerValidator creates a new handlers.BulkMessageHandlerValidator validator @@ -38,12 +40,14 @@ func NewBulkMessageHandlerValidator( tracer telemetry.Tracer, phoneService *services.PhoneService, userService *services.UserService, + appCache cache.Cache, ) (v *BulkMessageHandlerValidator) { return &BulkMessageHandlerValidator{ logger: logger.WithService(fmt.Sprintf("%T", v)), tracer: tracer, userService: userService, phoneService: phoneService, + cache: appCache, } } @@ -70,8 +74,8 @@ func (v *BulkMessageHandlerValidator) ValidateStore(ctx context.Context, userID return messages, result } - if len(messages) > 100 { - result.Add("document", "The uploaded file must contain less than 100 records.") + if len(messages) > 1000 { + result.Add("document", "The uploaded file must contain less than 1000 records.") return messages, result } @@ -79,7 +83,7 @@ func (v *BulkMessageHandlerValidator) ValidateStore(ctx context.Context, userID messages[index] = message.Sanitize() } - result = v.validateMessages(messages) + result = v.validateMessages(ctx, messages) if len(result) != 0 { return messages, result } @@ -119,6 +123,7 @@ func (v *BulkMessageHandlerValidator) parseXlsx(ctxLogger telemetry.Logger, user result.Add("document", fmt.Sprintf("Cannot parse the uploaded excel file with name [%s].", header.Filename)) return nil, result } + defer excel.Close() rows, err := excel.GetRows(excel.GetSheetName(0)) if err != nil { @@ -143,11 +148,17 @@ func (v *BulkMessageHandlerValidator) parseXlsx(ctxLogger telemetry.Logger, user } } + var attachmentURLs string + if len(row) > 4 && strings.TrimSpace(row[4]) != "" { + attachmentURLs = strings.TrimSpace(row[4]) + } + messages = append(messages, &requests.BulkMessage{ FromPhoneNumber: strings.TrimSpace(row[0]), ToPhoneNumber: strings.TrimSpace(row[1]), Content: row[2], SendTime: sendAt, + AttachmentURLs: attachmentURLs, }) } @@ -202,16 +213,46 @@ func (v *BulkMessageHandlerValidator) parseCSV(ctxLogger telemetry.Logger, user var messages []*requests.BulkMessage if err := csvutil.Unmarshal(content, &messages); err != nil { ctxLogger.Error(stacktrace.Propagate(err, fmt.Sprintf("cannot unmarshall contents [%s] into type [%T] for file [%s] and user [%s]", content, messages, header.Filename, user.ID))) - result.Add("document", fmt.Sprintf("Cannot read the conents of the uploaded file [%s].", header.Filename)) + result.Add("document", fmt.Sprintf("Cannot read the contents of the uploaded file [%s].", header.Filename)) return nil, result } return messages, url.Values{} } -func (v *BulkMessageHandlerValidator) validateMessages(messages []*requests.BulkMessage) url.Values { +func (v *BulkMessageHandlerValidator) validateMessages(_ context.Context, messages []*requests.BulkMessage) url.Values { result := url.Values{} for index, message := range messages { + + if message.AttachmentURLs != "" { + urls := strings.Split(message.AttachmentURLs, ",") + + validAttachmentCount := 0 + for _, u := range urls { + if strings.TrimSpace(u) != "" { + validAttachmentCount++ + } + } + + if validAttachmentCount > 10 { + result.Add("document", fmt.Sprintf("Row [%d]: You cannot attach more than 10 files per message.", index+2)) + } + + for _, u := range urls { + cleanURL := strings.TrimSpace(u) + if cleanURL == "" { + continue + } + + parsedURL, err := url.ParseRequestURI(cleanURL) + if err != nil || parsedURL.Scheme == "" || parsedURL.Host == "" { + result.Add("document", fmt.Sprintf("Row [%d]: The attachment URL [%s] has an invalid url format.", index+2, cleanURL)) + } else if parsedURL.Scheme != "http" && parsedURL.Scheme != "https" { + result.Add("document", fmt.Sprintf("Row [%d]: The attachment URL [%s] must use http or https.", index+2, cleanURL)) + } + } + } + if _, err := phonenumbers.Parse(message.FromPhoneNumber, phonenumbers.UNKNOWN_REGION); err != nil { result.Add("document", fmt.Sprintf("Row [%d]: The FromPhoneNumber [%s] is not a valid E.164 phone number", index+2, message.FromPhoneNumber)) } @@ -224,8 +265,8 @@ func (v *BulkMessageHandlerValidator) validateMessages(messages []*requests.Bulk result.Add("document", fmt.Sprintf("Row [%d]: The message content must be less than 1024 characters.", index+2)) } - if message.SendTime != nil && message.SendTime.After(time.Now().Add(24*time.Hour)) { - result.Add("document", fmt.Sprintf("Row [%d]: The SendTime [%s] cannot be more than 24 hours in the future.", index+2, message.SendTime.Format(time.RFC3339))) + if message.SendTime != nil && message.SendTime.After(time.Now().Add(420*time.Hour)) { + result.Add("document", fmt.Sprintf("Row [%d]: The SendTime [%s] cannot be more than 20 days (420 hours) in the future.", index+2, message.SendTime.Format(time.RFC3339))) } } return result diff --git a/api/pkg/validators/heartbeat_handler_validator.go b/api/pkg/validators/heartbeat_handler_validator.go index 6811dfd0..0472a475 100644 --- a/api/pkg/validators/heartbeat_handler_validator.go +++ b/api/pkg/validators/heartbeat_handler_validator.go @@ -62,9 +62,11 @@ func (validator *HeartbeatHandlerValidator) ValidateStore(_ context.Context, req v := govalidator.New(govalidator.Options{ Data: &request, Rules: govalidator.MapData{ - "owner": []string{ + "phone_numbers": []string{ "required", - phoneNumberRule, + "max:2", + "min:1", + multiplePhoneNumberRule, }, }, }) diff --git a/api/pkg/validators/message_handler_validator.go b/api/pkg/validators/message_handler_validator.go index 4635f216..ee6cf9b2 100644 --- a/api/pkg/validators/message_handler_validator.go +++ b/api/pkg/validators/message_handler_validator.go @@ -2,10 +2,13 @@ package validators import ( "context" + "encoding/base64" "fmt" "net/url" "strings" + "time" + "github.com/NdoleStudio/httpsms/pkg/cache" "github.com/NdoleStudio/httpsms/pkg/repositories" "github.com/NdoleStudio/httpsms/pkg/services" "github.com/palantir/stacktrace" @@ -20,9 +23,11 @@ import ( // MessageHandlerValidator validates models used in handlers.MessageHandler type MessageHandlerValidator struct { validator - logger telemetry.Logger - tracer telemetry.Tracer - phoneService *services.PhoneService + logger telemetry.Logger + tracer telemetry.Tracer + phoneService *services.PhoneService + tokenValidator *TurnstileTokenValidator + cache cache.Cache } // NewMessageHandlerValidator creates a new handlers.MessageHandler validator @@ -30,14 +35,24 @@ func NewMessageHandlerValidator( logger telemetry.Logger, tracer telemetry.Tracer, phoneService *services.PhoneService, + tokenValidator *TurnstileTokenValidator, + appCache cache.Cache, ) (v *MessageHandlerValidator) { return &MessageHandlerValidator{ - logger: logger.WithService(fmt.Sprintf("%T", v)), - tracer: tracer, - phoneService: phoneService, + logger: logger.WithService(fmt.Sprintf("%T", v)), + tracer: tracer, + phoneService: phoneService, + tokenValidator: tokenValidator, + cache: appCache, } } +const ( + maxAttachmentCount = 10 + maxAttachmentSize = (3 * 1024 * 1024) / 2 // 1.5 MB per attachment + maxTotalAttachmentSize = 3 * 1024 * 1024 // 3 MB total +) + // ValidateMessageReceive validates the requests.MessageReceive request func (validator MessageHandlerValidator) ValidateMessageReceive(_ context.Context, request requests.MessageReceive) url.Values { v := govalidator.New(govalidator.Options{ @@ -50,11 +65,12 @@ func (validator MessageHandlerValidator) ValidateMessageReceive(_ context.Contex "from": []string{ "required", }, - "content": []string{ - "required", - "min:1", - "max:1024", - }, + "content": func() []string { + if len(request.Attachments) > 0 { + return []string{"max:2048"} + } + return []string{"required", "min:1", "max:2048"} + }(), "sim": []string{ "required", "in:" + strings.Join([]string{ @@ -65,7 +81,54 @@ func (validator MessageHandlerValidator) ValidateMessageReceive(_ context.Contex }, }) - return v.ValidateStruct() + errors := v.ValidateStruct() + + if len(request.Attachments) > 0 { + attachmentErrors := validator.validateAttachments(request.Attachments) + for key, values := range attachmentErrors { + for _, value := range values { + errors.Add(key, value) + } + } + } + + return errors +} + +func (validator MessageHandlerValidator) validateAttachments(attachments []requests.MessageAttachment) url.Values { + errors := url.Values{} + allowedTypes := repositories.AllowedContentTypes() + + if len(attachments) > maxAttachmentCount { + errors.Add("attachments", fmt.Sprintf("attachment count [%d] exceeds maximum of [%d]", len(attachments), maxAttachmentCount)) + return errors + } + + totalSize := 0 + for i, attachment := range attachments { + if !allowedTypes[attachment.ContentType] { + errors.Add("attachments", fmt.Sprintf("attachment [%d] has unsupported content type [%s]", i, attachment.ContentType)) + continue + } + + decoded, err := base64.StdEncoding.DecodeString(attachment.Content) + if err != nil { + errors.Add("attachments", fmt.Sprintf("attachment [%d] has invalid base64 content", i)) + continue + } + + if len(decoded) > maxAttachmentSize { + errors.Add("attachments", fmt.Sprintf("attachment [%d] size [%d] exceeds maximum of [%d] bytes", i, len(decoded), maxAttachmentSize)) + } + + totalSize += len(decoded) + } + + if totalSize > maxTotalAttachmentSize { + errors.Add("attachments", fmt.Sprintf("total attachment size [%d] exceeds maximum of [%d] bytes", totalSize, maxTotalAttachmentSize)) + } + + return errors } // ValidateMessageSend validates the requests.MessageSend request @@ -89,10 +152,14 @@ func (validator MessageHandlerValidator) ValidateMessageSend(ctx context.Context "required", phoneNumberRule, }, + "attachments": []string{ + "max:10", + multipleAttachmentURLRule, + }, "content": []string{ "required", "min:1", - "max:1024", + "max:2048", }, }, }) @@ -102,6 +169,10 @@ func (validator MessageHandlerValidator) ValidateMessageSend(ctx context.Context return result } + if request.SendAt != nil && request.SendAt.After(time.Now().Add(480*time.Hour)) { + result.Add("send_at", "the scheduled time cannot be more than 20 days (480 hours) in the future") + } + _, err := validator.phoneService.Load(ctx, userID, request.From) if stacktrace.GetCode(err) == repositories.ErrCodeNotFound { result.Add("from", fmt.Sprintf("no phone found with with 'from' number [%s]. install the android app on your phone to start sending messages", request.From)) @@ -135,6 +206,10 @@ func (validator MessageHandlerValidator) ValidateMessageBulkSend(ctx context.Con "required", phoneNumberRule, }, + "attachments": []string{ + "max:10", + multipleAttachmentURLRule, + }, "content": []string{ "required", "min:1", @@ -207,6 +282,72 @@ func (validator MessageHandlerValidator) ValidateMessageIndex(_ context.Context, return v.ValidateStruct() } +// ValidateMessageSearch validates the requests.MessageSearch request +func (validator MessageHandlerValidator) ValidateMessageSearch(ctx context.Context, request requests.MessageSearch) url.Values { + v := govalidator.New(govalidator.Options{ + Data: &request, + Rules: govalidator.MapData{ + "owners": []string{ + multipleContactPhoneNumberRule, + }, + "types": []string{ + multipleInRule + ":" + strings.Join([]string{ + entities.MessageTypeCallMissed, + entities.MessageTypeMobileOriginated, + entities.MessageTypeMobileTerminated, + }, ","), + }, + "statuses": []string{ + multipleInRule + ":" + strings.Join([]string{ + entities.MessageStatusPending, + entities.MessageStatusSent, + entities.MessageStatusDelivered, + entities.MessageStatusFailed, + entities.MessageStatusExpired, + entities.MessageStatusReceived, + }, ","), + }, + "sort_by": []string{ + "in:" + strings.Join([]string{ + "created_at", + "owner", + "contact", + "type", + "status", + }, ","), + }, + "limit": []string{ + "required", + "numeric", + "min:1", + "max:200", + }, + "skip": []string{ + "required", + "numeric", + "min:0", + }, + "query": []string{ + "max:20", + }, + "token": []string{ + "required", + }, + }, + }) + + errors := v.ValidateStruct() + if len(errors) > 0 { + return errors + } + + if !validator.tokenValidator.ValidateToken(ctx, request.IPAddress, request.Token) { + errors.Add("token", "The captcha token from turnstile is invalid") + } + + return errors +} + // ValidateMessageEvent validates the requests.MessageEvent request func (validator MessageHandlerValidator) ValidateMessageEvent(_ context.Context, request requests.MessageEvent) url.Values { v := govalidator.New(govalidator.Options{ @@ -228,3 +369,28 @@ func (validator MessageHandlerValidator) ValidateMessageEvent(_ context.Context, }) return v.ValidateStruct() } + +// ValidateCallMissed validates the requests.MessageCallMissed request +func (validator MessageHandlerValidator) ValidateCallMissed(_ context.Context, request requests.MessageCallMissed) url.Values { + v := govalidator.New(govalidator.Options{ + Data: &request, + Rules: govalidator.MapData{ + "to": []string{ + "required", + phoneNumberRule, + }, + "from": []string{ + "required", + }, + "sim": []string{ + "required", + "in:" + strings.Join([]string{ + string(entities.SIM1), + string(entities.SIM2), + }, ","), + }, + }, + }) + + return v.ValidateStruct() +} diff --git a/api/pkg/validators/phone_api_key_handler_validator.go b/api/pkg/validators/phone_api_key_handler_validator.go new file mode 100644 index 00000000..459e7b3a --- /dev/null +++ b/api/pkg/validators/phone_api_key_handler_validator.go @@ -0,0 +1,69 @@ +package validators + +import ( + "context" + "fmt" + "net/url" + + "github.com/NdoleStudio/httpsms/pkg/requests" + "github.com/NdoleStudio/httpsms/pkg/telemetry" + "github.com/thedevsaddam/govalidator" +) + +// PhoneAPIKeyHandlerValidator validates models used in handlers.PhoneAPIKeyHandler +type PhoneAPIKeyHandlerValidator struct { + validator + logger telemetry.Logger + tracer telemetry.Tracer +} + +// NewPhoneAPIKeyHandlerValidator creates a new handlers.PhoneAPIKeyHandler validator +func NewPhoneAPIKeyHandlerValidator( + logger telemetry.Logger, + tracer telemetry.Tracer, +) (v *PhoneAPIKeyHandlerValidator) { + return &PhoneAPIKeyHandlerValidator{ + logger: logger.WithService(fmt.Sprintf("%T", v)), + tracer: tracer, + } +} + +// ValidateStore validates requests.PhoneAPIKeyStoreRequest +func (validator *PhoneAPIKeyHandlerValidator) ValidateStore(_ context.Context, request requests.PhoneAPIKeyStoreRequest) url.Values { + v := govalidator.New(govalidator.Options{ + Data: &request, + Rules: govalidator.MapData{ + "name": []string{ + "required", + "min:1", + "max:60", + }, + }, + }) + + return v.ValidateStruct() +} + +// ValidateIndex validates the requests.HeartbeatIndex request +func (validator *PhoneAPIKeyHandlerValidator) ValidateIndex(_ context.Context, request requests.PhoneAPIKeyIndex) url.Values { + v := govalidator.New(govalidator.Options{ + Data: &request, + Rules: govalidator.MapData{ + "limit": []string{ + "required", + "numeric", + "min:1", + "max:100", + }, + "skip": []string{ + "required", + "numeric", + "min:0", + }, + "query": []string{ + "max:100", + }, + }, + }) + return v.ValidateStruct() +} diff --git a/api/pkg/validators/phone_handler_validator.go b/api/pkg/validators/phone_handler_validator.go index 1beba13d..2369214e 100644 --- a/api/pkg/validators/phone_handler_validator.go +++ b/api/pkg/validators/phone_handler_validator.go @@ -99,6 +99,29 @@ func (validator *PhoneHandlerValidator) ValidateUpsert(_ context.Context, reques return result } +// ValidateFCMToken validates requests.PhoneFCMToken +func (validator *PhoneHandlerValidator) ValidateFCMToken(_ context.Context, request requests.PhoneFCMToken) url.Values { + v := govalidator.New(govalidator.Options{ + Data: &request, + Rules: govalidator.MapData{ + "phone_number": []string{ + "required", + phoneNumberRule, + }, + "fcm_token": []string{ + "min:0", + "max:1000", + }, + "sim": []string{ + "required", + "in:" + strings.Join([]string{entities.SIM1.String(), entities.SIM2.String()}, ","), + }, + }, + }) + + return v.ValidateStruct() +} + // ValidateDelete ValidateUpsert validates requests.PhoneDelete func (validator *PhoneHandlerValidator) ValidateDelete(_ context.Context, request requests.PhoneDelete) url.Values { v := govalidator.New(govalidator.Options{ diff --git a/api/pkg/validators/turnstile_token_validator.go b/api/pkg/validators/turnstile_token_validator.go new file mode 100644 index 00000000..788e6dd3 --- /dev/null +++ b/api/pkg/validators/turnstile_token_validator.go @@ -0,0 +1,91 @@ +package validators + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io" + "net/http" + "time" + + "github.com/NdoleStudio/httpsms/pkg/telemetry" + "github.com/palantir/stacktrace" +) + +// TurnstileTokenValidator validates the token used to validate captchas from cloudflare +type TurnstileTokenValidator struct { + logger telemetry.Logger + tracer telemetry.Tracer + secretKey string + httpClient *http.Client +} + +type turnstileVerifyResponse struct { + Success bool `json:"success"` + ChallengeTs time.Time `json:"challenge_ts"` + Hostname string `json:"hostname"` + ErrorCodes []any `json:"error-codes"` + Action string `json:"action"` + Cdata string `json:"cdata"` + Metadata struct { + EphemeralID string `json:"ephemeral_id"` + } `json:"metadata"` +} + +// NewTurnstileTokenValidator creates a new TurnstileTokenValidator +func NewTurnstileTokenValidator(logger telemetry.Logger, tracer telemetry.Tracer, secretKey string, httpClient *http.Client) *TurnstileTokenValidator { + return &TurnstileTokenValidator{ + logger.WithService(fmt.Sprintf("%T", &TurnstileTokenValidator{})), + tracer, + secretKey, + httpClient, + } +} + +// ValidateToken validates the cloudflare turnstile token +// https://developers.cloudflare.com/turnstile/get-started/server-side-validation/ +func (v *TurnstileTokenValidator) ValidateToken(ctx context.Context, ipAddress, token string) bool { + ctx, span, ctxLogger := v.tracer.StartWithLogger(ctx, v.logger) + defer span.End() + + payload, err := json.Marshal(map[string]string{ + "secret": v.secretKey, + "response": token, + "remoteip": ipAddress, + }) + if err != nil { + ctxLogger.Error(stacktrace.Propagate(err, "failed to marshal payload")) + return false + } + + request, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://challenges.cloudflare.com/turnstile/v0/siteverify", bytes.NewBuffer(payload)) + if err != nil { + ctxLogger.Error(stacktrace.Propagate(err, "failed to create http request request")) + return false + } + + request.Header.Set("Content-Type", "application/json") + response, err := v.httpClient.Do(request) + if err != nil { + ctxLogger.Error(stacktrace.Propagate(err, fmt.Sprintf("failed to send http request to [%s]", request.URL.String()))) + return false + } + defer response.Body.Close() + + body, err := io.ReadAll(response.Body) + if err != nil { + ctxLogger.Error(stacktrace.Propagate(err, "failed to read response body from cloudflare turnstile")) + return false + } + + ctxLogger.Info(fmt.Sprintf("successfully validated token with cloudflare with response [%s]", body)) + + data := new(turnstileVerifyResponse) + if err = json.Unmarshal(body, data); err != nil { + ctxLogger.Error(stacktrace.Propagate(err, "failed to unmarshal response from cloudflare turnstile")) + return false + } + + return data.Success +} diff --git a/api/pkg/validators/user_handler_validator.go b/api/pkg/validators/user_handler_validator.go index 1b90cdbc..4c05bd1b 100644 --- a/api/pkg/validators/user_handler_validator.go +++ b/api/pkg/validators/user_handler_validator.go @@ -5,26 +5,32 @@ import ( "fmt" "net/url" + "github.com/NdoleStudio/httpsms/pkg/entities" "github.com/NdoleStudio/httpsms/pkg/requests" + "github.com/NdoleStudio/httpsms/pkg/services" "github.com/NdoleStudio/httpsms/pkg/telemetry" + "github.com/palantir/stacktrace" "github.com/thedevsaddam/govalidator" ) // UserHandlerValidator validates models used in handlers.UserHandler type UserHandlerValidator struct { validator - logger telemetry.Logger - tracer telemetry.Tracer + logger telemetry.Logger + tracer telemetry.Tracer + service *services.UserService } // NewUserHandlerValidator creates a new handlers.UserHandler validator func NewUserHandlerValidator( logger telemetry.Logger, tracer telemetry.Tracer, + service *services.UserService, ) (v *UserHandlerValidator) { return &UserHandlerValidator{ - logger: logger.WithService(fmt.Sprintf("%T", v)), - tracer: tracer, + service: service, + logger: logger.WithService(fmt.Sprintf("%T", v)), + tracer: tracer, } } @@ -34,7 +40,6 @@ func (validator *UserHandlerValidator) ValidateUpdate(_ context.Context, request Data: &request, Rules: govalidator.MapData{ "active_phone_id": []string{ - "required", "uuid", }, }, @@ -42,3 +47,83 @@ func (validator *UserHandlerValidator) ValidateUpdate(_ context.Context, request return v.ValidateStruct() } + +// ValidatePaymentInvoice validates the requests.UserPaymentInvoice request +func (validator *UserHandlerValidator) ValidatePaymentInvoice(ctx context.Context, userID entities.UserID, request requests.UserPaymentInvoice) url.Values { + ctx, span, ctxLogger := validator.tracer.StartWithLogger(ctx, validator.logger) + defer span.End() + + rules := govalidator.MapData{ + "name": []string{ + "required", + "min:1", + "max:100", + }, + "address": []string{ + "required", + "min:1", + "max:200", + }, + "city": []string{ + "required", + "min:1", + "max:100", + }, + "state": []string{ + "min:1", + "max:100", + }, + "country": []string{ + "required", + "len:2", + }, + "zip_code": []string{ + "required", + "min:1", + "max:20", + }, + "notes": []string{ + "max:1000", + }, + } + if request.Country == "CA" { + rules["state"] = []string{ + "required", + "in:AB,BC,MB,NB,NL,NS,NT,NU,ON,PE,QC,SK,YT", + } + } + + if request.Country == "US" { + rules["state"] = []string{ + "required", + "in:AL,AK,AZ,AR,CA,CO,CT,DE,FL,GA,HI,ID,IL,IN,IA,KS,KY,LA,ME,MD,MA,MI,MN,MS,MO,MT,NE,NV,NH,NJ,NM,NY,NC,ND,OH,OK,OR,PA,RI,SC,SD,TN,TX,UT,VT,VA,WA,WV,WI,WY", + } + } + + v := govalidator.New(govalidator.Options{ + Data: &request, + Rules: rules, + }) + + validationErrors := v.ValidateStruct() + if len(validationErrors) > 0 { + return validationErrors + } + + payments, err := validator.service.GetSubscriptionPayments(ctx, userID) + if err != nil { + msg := fmt.Sprintf("cannot get subscription payments for user with ID [%s]", userID) + ctxLogger.Error(validator.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg))) + validationErrors.Add("subscriptionInvoiceID", "failed to validate subscription payment invoice ID") + return validationErrors + } + + for _, payment := range payments { + if payment.ID == request.SubscriptionInvoiceID { + return validationErrors + } + } + + validationErrors.Add("subscriptionInvoiceID", "failed to validate the subscription payment invoice ID") + return validationErrors +} diff --git a/api/pkg/validators/validator.go b/api/pkg/validators/validator.go index dedf3be1..1fcb716a 100644 --- a/api/pkg/validators/validator.go +++ b/api/pkg/validators/validator.go @@ -3,9 +3,13 @@ package validators import ( "context" "fmt" + "net/http" "net/url" "regexp" + "strings" + "time" + "github.com/NdoleStudio/httpsms/pkg/cache" "github.com/NdoleStudio/httpsms/pkg/events" "github.com/nyaruka/phonenumbers" @@ -16,8 +20,11 @@ type validator struct{} const ( phoneNumberRule = "phoneNumber" + multiplePhoneNumberRule = "multiplePhoneNumber" contactPhoneNumberRule = "contactPhoneNumber" multipleContactPhoneNumberRule = "multipleContactPhoneNumber" + multipleAttachmentURLRule = "multipleAttachmentURL" + multipleInRule = "multipleIn" webhookEventsRule = "webhookEvents" ) @@ -27,12 +34,28 @@ func init() { govalidator.AddCustomRule(phoneNumberRule, func(field string, rule string, message string, value interface{}) error { phoneNumber, ok := value.(string) if !ok { - return fmt.Errorf("The %s field must be a valid E.164 phone number: https://en.wikipedia.org/wiki/E.164", field) + return fmt.Errorf("The %s field must be a valid E.164 phone number in the international format e.g +18005550100", field) } _, err := phonenumbers.Parse(phoneNumber, phonenumbers.UNKNOWN_REGION) if err != nil { - return fmt.Errorf("The %s field must be a valid E.164 phone number: https://en.wikipedia.org/wiki/E.164", field) + return fmt.Errorf("The %s field must be a valid E.164 phone number in the international format e.g +18005550100", field) + } + + return nil + }) + + govalidator.AddCustomRule(multiplePhoneNumberRule, func(field string, rule string, message string, value interface{}) error { + phoneNumbers, ok := value.([]string) + if !ok { + return fmt.Errorf("The %s field must be an array of valid phone numbers", field) + } + + for index, number := range phoneNumbers { + _, err := phonenumbers.Parse(number, phonenumbers.UNKNOWN_REGION) + if err != nil { + return fmt.Errorf("The %s field in index [%d] must be a valid E.164 phone number in the international format e.g +18005550100", field, index) + } } return nil @@ -68,6 +91,46 @@ func init() { return nil }) + govalidator.AddCustomRule(multipleAttachmentURLRule, func(field string, rule string, message string, value interface{}) error { + attachments, ok := value.([]string) + if !ok { + return fmt.Errorf("The %s field must be an array of valid attachment URLs", field) + } + + for index, attachment := range attachments { + u, err := url.ParseRequestURI(attachment) + if err != nil || (u.Scheme != "http" && u.Scheme != "https") || u.Host == "" { + return fmt.Errorf("The attachment %d with URL [%s] must be a valid URL e.g https://placehold.co/600x400", index, attachment) + } + } + return nil + }) + + govalidator.AddCustomRule(multipleInRule, func(field string, rule string, message string, value interface{}) error { + values, ok := value.([]string) + if !ok { + return fmt.Errorf("the %s field must be a string array", field) + } + + allowlist := strings.Split(strings.TrimPrefix(rule, multipleInRule+":"), ",") + contains := func(str string) bool { + for _, a := range allowlist { + if a == str { + return true + } + } + return false + } + + for index, item := range values { + if !contains(item) { + return fmt.Errorf("the %s field in contains an invalid value [%s] at index [%d]", field, item, index) + } + } + + return nil + }) + govalidator.AddCustomRule(webhookEventsRule, func(field string, rule string, message string, value interface{}) error { input, ok := value.([]string) if !ok { @@ -84,6 +147,9 @@ func init() { events.EventTypeMessagePhoneDelivered: true, events.EventTypeMessageSendFailed: true, events.EventTypeMessageSendExpired: true, + events.EventTypePhoneHeartbeatOnline: true, + events.EventTypePhoneHeartbeatOffline: true, + events.MessageCallMissed: true, } for _, event := range input { @@ -97,7 +163,7 @@ func init() { } // ValidateUUID that the payload is a UUID -func (validator *validator) ValidateUUID(_ context.Context, ID string, name string) url.Values { +func (validator *validator) ValidateUUID(ID string, name string) url.Values { request := map[string]string{ name: ID, } @@ -114,3 +180,54 @@ func (validator *validator) ValidateUUID(_ context.Context, ID string, name stri return v.ValidateStruct() } + +func validateAttachmentURL(ctx context.Context, c cache.Cache, attachmentURL string) error { + cacheKey := "mms-url-validation:" + attachmentURL + + if cachedVal, err := c.Get(ctx, cacheKey); err == nil { + if cachedVal == "valid" { + return nil + } + return fmt.Errorf(cachedVal) + } + + client := &http.Client{ + Timeout: 5 * time.Second, + } + + req, err := http.NewRequest(http.MethodHead, attachmentURL, nil) + if err != nil { + errMsg := fmt.Sprintf("invalid url format") + saveToCache(ctx, c, cacheKey, errMsg) + return fmt.Errorf(errMsg) + } + + resp, err := client.Do(req) + if err != nil { + errMsg := fmt.Sprintf("could not reach the url") + saveToCache(ctx, c, cacheKey, errMsg) + return fmt.Errorf(errMsg) + } + defer resp.Body.Close() + + if resp.StatusCode < 200 || resp.StatusCode >= 400 { + errMsg := fmt.Sprintf("url returned an error status code: %d", resp.StatusCode) + saveToCache(ctx, c, cacheKey, errMsg) + return fmt.Errorf(errMsg) + } + + const maxSizeBytes = 1.5 * 1024 * 1024 + + if resp.ContentLength > int64(maxSizeBytes) { + errMsg := fmt.Sprintf("file size (%.2f MB) exceeds the 1.5 MB carrier limit", float64(resp.ContentLength)/(1024*1024)) + saveToCache(ctx, c, cacheKey, errMsg) + return fmt.Errorf(errMsg) + } + + saveToCache(ctx, c, cacheKey, "valid") + return nil +} + +func saveToCache(ctx context.Context, c cache.Cache, key string, value string) { + _ = c.Set(ctx, key, value, 15*time.Minute) +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..ef63287d --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,53 @@ +services: + postgres: + image: postgres:alpine + environment: + POSTGRES_DB: httpsms + POSTGRES_PASSWORD: dbpassword + POSTGRES_USER: dbusername + volumes: + - postgres:/var/lib/postgresql/data + ports: + - "5435:5432" + restart: on-failure + healthcheck: + test: ["CMD-SHELL", "pg_isready -U $${POSTGRES_USER} -d $${POSTGRES_DB}"] + interval: 30s + timeout: 60s + retries: 5 + start_period: 5s + + redis: + image: redis:latest + command: redis-server + volumes: + - redis:/var/lib/redis + ports: + - "6379:6379" + restart: on-failure + + api: + build: + context: ./api + ports: + - "8000:8000" + depends_on: + postgres: + condition: service_healthy + redis: + condition: service_started + env_file: + - ./api/.env + + web: + build: + context: ./web + ports: + - "3000:3000" + depends_on: + api: + condition: service_started + +volumes: + redis: + postgres: diff --git a/docs/superpowers/plans/2026-04-11-mms-attachments.md b/docs/superpowers/plans/2026-04-11-mms-attachments.md new file mode 100644 index 00000000..a759e1c4 --- /dev/null +++ b/docs/superpowers/plans/2026-04-11-mms-attachments.md @@ -0,0 +1,1139 @@ +# MMS Attachment Support Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Add MMS attachment upload/download support to the httpSMS API so received MMS attachments are stored in cloud storage and downloadable via a public URL. + +**Architecture:** Android sends base64-encoded attachments in the receive request. The API decodes and uploads them to GCS (or in-memory storage) via a storage interface, stores download URLs in the existing `Message.Attachments` field, and exposes an unauthenticated download endpoint. The webhook event payload includes attachment URLs. + +**Tech Stack:** Go, Fiber v2, GORM, `cloud.google.com/go/storage`, `errgroup`, `stacktrace` + +--- + +## File Structure + +**New files:** + +| File | Responsibility | +| --------------------------------------------------- | -------------------------------------------------------------------------------- | +| `api/pkg/repositories/attachment_storage.go` | `AttachmentStorage` interface + content-type-to-extension mapping + sanitization | +| `api/pkg/repositories/gcs_attachment_storage.go` | GCS implementation of `AttachmentStorage` | +| `api/pkg/repositories/memory_attachment_storage.go` | In-memory implementation of `AttachmentStorage` | +| `api/pkg/repositories/attachment_storage_test.go` | Unit tests for content-type mapping and filename sanitization | +| `api/pkg/handlers/attachment_handler.go` | Download endpoint handler (`GET /v1/attachments/...`) | + +**Modified files:** + +| File | Change | +| ------------------------------------------------- | ------------------------------------------------------------------------------- | +| `api/pkg/requests/message_receive_request.go` | Add `Attachments` field + `MessageAttachment` struct | +| `api/pkg/services/message_service.go` | Add `Attachments` to params, upload logic in `ReceiveMessage()`, set on message | +| `api/pkg/validators/message_handler_validator.go` | Add attachment count/size/content-type validation | +| `api/pkg/events/message_phone_received_event.go` | Add `Attachments []string` to payload | +| `api/pkg/di/container.go` | Wire `AttachmentStorage`, `AttachmentHandler`, `RegisterAttachmentRoutes()` | +| `api/.env.docker` | Add `GCS_BUCKET_NAME` | +| `api/go.mod` / `api/go.sum` | Add `cloud.google.com/go/storage` and `golang.org/x/sync` (errgroup) | + +--- + +### Task 1: Add GCS SDK and errgroup dependencies + +**Files:** + +- Modify: `api/go.mod` + +- [ ] **Step 1: Add dependencies** + +```bash +cd api && go get cloud.google.com/go/storage && go get golang.org/x/sync +``` + +- [ ] **Step 2: Verify build still works** + +Run: `cd api && go build ./...` +Expected: Build succeeds + +- [ ] **Step 3: Commit** + +```bash +cd api && git add go.mod go.sum && git commit -m "chore: add cloud.google.com/go/storage and golang.org/x/sync deps" +``` + +--- + +### Task 2: Storage interface, content-type mapping, and filename sanitization + +**Files:** + +- Create: `api/pkg/repositories/attachment_storage.go` +- Create: `api/pkg/repositories/attachment_storage_test.go` + +- [ ] **Step 1: Write the test file** + +Create `api/pkg/repositories/attachment_storage_test.go`: + +```go +package repositories + +import "testing" + +func TestExtensionFromContentType(t *testing.T) { + tests := []struct { + contentType string + expected string + }{ + {"image/jpeg", ".jpg"}, + {"image/png", ".png"}, + {"image/gif", ".gif"}, + {"image/webp", ".webp"}, + {"image/bmp", ".bmp"}, + {"video/mp4", ".mp4"}, + {"video/3gpp", ".3gp"}, + {"audio/mpeg", ".mp3"}, + {"audio/ogg", ".ogg"}, + {"audio/amr", ".amr"}, + {"application/pdf", ".pdf"}, + {"text/vcard", ".vcf"}, + {"text/x-vcard", ".vcf"}, + {"application/octet-stream", ".bin"}, + {"unknown/type", ".bin"}, + {"", ".bin"}, + } + for _, tt := range tests { + t.Run(tt.contentType, func(t *testing.T) { + got := ExtensionFromContentType(tt.contentType) + if got != tt.expected { + t.Errorf("ExtensionFromContentType(%q) = %q, want %q", tt.contentType, got, tt.expected) + } + }) + } +} + +func TestSanitizeFilename(t *testing.T) { + tests := []struct { + name string + index int + expected string + }{ + {"photo.jpg", 0, "photo"}, + {"../../etc/passwd", 0, "etcpasswd"}, + {"hello/world\\test", 0, "helloworldtest"}, + {"normal_file", 0, "normal_file"}, + {"", 0, "attachment-0"}, + {" ", 0, "attachment-0"}, + {"...", 1, "attachment-1"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got := SanitizeFilename(tt.name, tt.index) + if got != tt.expected { + t.Errorf("SanitizeFilename(%q, %d) = %q, want %q", tt.name, tt.index, got, tt.expected) + } + }) + } +} +``` + +- [ ] **Step 2: Run tests to verify they fail** + +Run: `cd api && go test ./pkg/repositories/ -run "TestExtensionFromContentType|TestSanitizeFilename" -v` +Expected: FAIL — functions not defined + +- [ ] **Step 3: Write the storage interface and utility functions** + +Create `api/pkg/repositories/attachment_storage.go`: + +```go +package repositories + +import ( + "context" + "fmt" + "path/filepath" + "strings" +) + +// AttachmentStorage is the interface for storing and retrieving message attachments +type AttachmentStorage interface { + // Upload stores attachment data at the given path + Upload(ctx context.Context, path string, data []byte) error + // Download retrieves attachment data from the given path + Download(ctx context.Context, path string) ([]byte, error) + // Delete removes an attachment at the given path + Delete(ctx context.Context, path string) error +} + +// contentTypeExtensions maps MIME types to file extensions +var contentTypeExtensions = map[string]string{ + "image/jpeg": ".jpg", + "image/png": ".png", + "image/gif": ".gif", + "image/webp": ".webp", + "image/bmp": ".bmp", + "video/mp4": ".mp4", + "video/3gpp": ".3gp", + "audio/mpeg": ".mp3", + "audio/ogg": ".ogg", + "audio/amr": ".amr", + "application/pdf": ".pdf", + "text/vcard": ".vcf", + "text/x-vcard": ".vcf", +} + +// AllowedContentTypes returns the set of allowed MIME types for attachments +func AllowedContentTypes() map[string]bool { + allowed := make(map[string]bool, len(contentTypeExtensions)) + for ct := range contentTypeExtensions { + allowed[ct] = true + } + return allowed +} + +// ExtensionFromContentType returns the file extension for a MIME content type. +// Returns ".bin" if the content type is not recognized. +func ExtensionFromContentType(contentType string) string { + if ext, ok := contentTypeExtensions[contentType]; ok { + return ext + } + return ".bin" +} + +// ContentTypeFromExtension returns the MIME content type for a file extension. +// Returns "application/octet-stream" if the extension is not recognized. +func ContentTypeFromExtension(ext string) string { + for ct, e := range contentTypeExtensions { + if e == ext { + return ct + } + } + return "application/octet-stream" +} + +// SanitizeFilename removes path separators and traversal sequences from a filename. +// Returns "attachment-{index}" if the sanitized name is empty. +func SanitizeFilename(name string, index int) string { + name = strings.TrimSuffix(name, filepath.Ext(name)) + name = strings.ReplaceAll(name, "/", "") + name = strings.ReplaceAll(name, "\\", "") + name = strings.ReplaceAll(name, "..", "") + name = strings.TrimSpace(name) + + if name == "" { + return fmt.Sprintf("attachment-%d", index) + } + return name +} +``` + +- [ ] **Step 4: Run tests to verify they pass** + +Run: `cd api && go test ./pkg/repositories/ -run "TestExtensionFromContentType|TestSanitizeFilename" -v` +Expected: PASS + +- [ ] **Step 5: Commit** + +```bash +cd api && git add -A && git commit -m "feat: add AttachmentStorage interface and content-type utilities" +``` + +--- + +### Task 3: Memory storage implementation + +**Files:** + +- Create: `api/pkg/repositories/memory_attachment_storage.go` + +- [ ] **Step 1: Write the implementation** + +Create `api/pkg/repositories/memory_attachment_storage.go`: + +```go +package repositories + +import ( + "context" + "fmt" + "sync" + + "github.com/NdoleStudio/httpsms/pkg/telemetry" + "github.com/palantir/stacktrace" +) + +// MemoryAttachmentStorage stores attachments in memory +type MemoryAttachmentStorage struct { + logger telemetry.Logger + tracer telemetry.Tracer + data sync.Map +} + +// NewMemoryAttachmentStorage creates a new MemoryAttachmentStorage +func NewMemoryAttachmentStorage( + logger telemetry.Logger, + tracer telemetry.Tracer, +) *MemoryAttachmentStorage { + return &MemoryAttachmentStorage{ + logger: logger.WithService(fmt.Sprintf("%T", &MemoryAttachmentStorage{})), + tracer: tracer, + } +} + +// Upload stores attachment data at the given path +func (s *MemoryAttachmentStorage) Upload(ctx context.Context, path string, data []byte) error { + _, span := s.tracer.Start(ctx) + defer span.End() + + s.data.Store(path, data) + s.logger.Info(fmt.Sprintf("stored attachment at path [%s] with size [%d]", path, len(data))) + return nil +} + +// Download retrieves attachment data from the given path +func (s *MemoryAttachmentStorage) Download(ctx context.Context, path string) ([]byte, error) { + _, span := s.tracer.Start(ctx) + defer span.End() + + value, ok := s.data.Load(path) + if !ok { + return nil, stacktrace.NewError(fmt.Sprintf("attachment not found at path [%s]", path)) + } + return value.([]byte), nil +} + +// Delete removes an attachment at the given path +func (s *MemoryAttachmentStorage) Delete(ctx context.Context, path string) error { + _, span := s.tracer.Start(ctx) + defer span.End() + + s.data.Delete(path) + s.logger.Info(fmt.Sprintf("deleted attachment at path [%s]", path)) + return nil +} +``` + +- [ ] **Step 2: Verify build** + +Run: `cd api && go build ./...` +Expected: Build succeeds + +- [ ] **Step 3: Commit** + +```bash +cd api && git add -A && git commit -m "feat: add MemoryAttachmentStorage implementation" +``` + +--- + +### Task 4: GCS storage implementation + +**Files:** + +- Create: `api/pkg/repositories/gcs_attachment_storage.go` + +- [ ] **Step 1: Write the implementation** + +Create `api/pkg/repositories/gcs_attachment_storage.go`: + +```go +package repositories + +import ( + "context" + "fmt" + "io" + + "cloud.google.com/go/storage" + "github.com/NdoleStudio/httpsms/pkg/telemetry" + "github.com/palantir/stacktrace" +) + +// GCSAttachmentStorage stores attachments in Google Cloud Storage +type GCSAttachmentStorage struct { + logger telemetry.Logger + tracer telemetry.Tracer + client *storage.Client + bucket string +} + +// NewGCSAttachmentStorage creates a new GCSAttachmentStorage +func NewGCSAttachmentStorage( + logger telemetry.Logger, + tracer telemetry.Tracer, + client *storage.Client, + bucket string, +) *GCSAttachmentStorage { + return &GCSAttachmentStorage{ + logger: logger.WithService(fmt.Sprintf("%T", &GCSAttachmentStorage{})), + tracer: tracer, + client: client, + bucket: bucket, + } +} + +// Upload stores attachment data at the given path in GCS +func (s *GCSAttachmentStorage) Upload(ctx context.Context, path string, data []byte) error { + ctx, span := s.tracer.Start(ctx) + defer span.End() + + writer := s.client.Bucket(s.bucket).Object(path).NewWriter(ctx) + if _, err := writer.Write(data); err != nil { + return s.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, fmt.Sprintf("cannot write attachment to GCS path [%s]", path))) + } + + if err := writer.Close(); err != nil { + return s.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, fmt.Sprintf("cannot close GCS writer for path [%s]", path))) + } + + s.logger.Info(fmt.Sprintf("uploaded attachment to GCS path [%s/%s] with size [%d]", s.bucket, path, len(data))) + return nil +} + +// Download retrieves attachment data from the given path in GCS +func (s *GCSAttachmentStorage) Download(ctx context.Context, path string) ([]byte, error) { + ctx, span := s.tracer.Start(ctx) + defer span.End() + + reader, err := s.client.Bucket(s.bucket).Object(path).NewReader(ctx) + if err != nil { + return nil, s.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, fmt.Sprintf("cannot open GCS reader for path [%s]", path))) + } + defer reader.Close() + + data, err := io.ReadAll(reader) + if err != nil { + return nil, s.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, fmt.Sprintf("cannot read attachment from GCS path [%s]", path))) + } + + return data, nil +} + +// Delete removes an attachment at the given path in GCS +func (s *GCSAttachmentStorage) Delete(ctx context.Context, path string) error { + ctx, span := s.tracer.Start(ctx) + defer span.End() + + if err := s.client.Bucket(s.bucket).Object(path).Delete(ctx); err != nil { + return s.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, fmt.Sprintf("cannot delete GCS object at path [%s]", path))) + } + + s.logger.Info(fmt.Sprintf("deleted attachment from GCS path [%s/%s]", s.bucket, path)) + return nil +} +``` + +- [ ] **Step 2: Verify build** + +Run: `cd api && go build ./...` +Expected: Build succeeds + +- [ ] **Step 3: Commit** + +```bash +cd api && git add -A && git commit -m "feat: add GCSAttachmentStorage implementation" +``` + +--- + +### Task 5: Update request and event structs + +**Files:** + +- Modify: `api/pkg/requests/message_receive_request.go` (full file) +- Modify: `api/pkg/services/message_service.go:290-300` (MessageReceiveParams) +- Modify: `api/pkg/events/message_phone_received_event.go:14-23` (payload struct) + +**Important:** The `requests` package already imports `services` (for `ToMessageReceiveParams`), so we **cannot** import `requests` from `services`. Define a `ServiceAttachment` struct in the services package to avoid a circular import. + +- [ ] **Step 1: Add ServiceAttachment to services package** + +In `api/pkg/services/message_service.go`, add after the imports (before `MessageService` struct at line 22): + +```go +// ServiceAttachment represents attachment data passed to the service layer +type ServiceAttachment struct { + Name string + ContentType string + Content string // base64-encoded +} +``` + +Update `MessageReceiveParams` (lines 290-300) to add `Attachments`: + +```go +type MessageReceiveParams struct { + Contact string + UserID entities.UserID + Owner phonenumbers.PhoneNumber + Content string + SIM entities.SIM + Timestamp time.Time + Encrypted bool + Source string + Attachments []ServiceAttachment +} +``` + +- [ ] **Step 2: Add MessageAttachment struct and update MessageReceive request** + +In `api/pkg/requests/message_receive_request.go`, add the `MessageAttachment` struct before the `MessageReceive` struct, and add the `Attachments` field: + +```go +// MessageAttachment represents a single MMS attachment in a receive request +type MessageAttachment struct { + // Name is the original filename of the attachment + Name string `json:"name" example:"photo.jpg"` + // ContentType is the MIME type of the attachment + ContentType string `json:"content_type" example:"image/jpeg"` + // Content is the base64-encoded attachment data + Content string `json:"content" example:"base64data..."` +} + +// MessageReceive is the payload for receiving an SMS/MMS message +type MessageReceive struct { + request + From string `json:"from" example:"+18005550199"` + To string `json:"to" example:"+18005550100"` + Content string `json:"content" example:"This is a sample text message received on a phone"` + // Encrypted is used to determine if the content is end-to-end encrypted + Encrypted bool `json:"encrypted" example:"false"` + // SIM card that received the message + SIM entities.SIM `json:"sim" example:"SIM1"` + // Timestamp is the time when the event was emitted + Timestamp time.Time `json:"timestamp" example:"2022-06-05T14:26:09.527976+03:00"` + // Attachments is the list of MMS attachments received with the message + Attachments []MessageAttachment `json:"attachments"` +} +``` + +Update `ToMessageReceiveParams` to convert attachments: + +```go +func (input *MessageReceive) ToMessageReceiveParams(userID entities.UserID, source string) *services.MessageReceiveParams { + phone, _ := phonenumbers.Parse(input.To, phonenumbers.UNKNOWN_REGION) + + attachments := make([]services.ServiceAttachment, len(input.Attachments)) + for i, a := range input.Attachments { + attachments[i] = services.ServiceAttachment{ + Name: a.Name, + ContentType: a.ContentType, + Content: a.Content, + } + } + + return &services.MessageReceiveParams{ + Source: source, + Contact: input.From, + UserID: userID, + Timestamp: input.Timestamp, + Encrypted: input.Encrypted, + Owner: *phone, + Content: input.Content, + SIM: input.SIM, + Attachments: attachments, + } +} +``` + +- [ ] **Step 3: Update MessagePhoneReceivedPayload** + +In `api/pkg/events/message_phone_received_event.go`, add `Attachments` field to the payload struct (after the `SIM` field): + +```go +type MessagePhoneReceivedPayload struct { + MessageID uuid.UUID `json:"message_id"` + UserID entities.UserID `json:"user_id"` + Owner string `json:"owner"` + Encrypted bool `json:"encrypted"` + Contact string `json:"contact"` + Timestamp time.Time `json:"timestamp"` + Content string `json:"content"` + SIM entities.SIM `json:"sim"` + Attachments []string `json:"attachments"` +} +``` + +- [ ] **Step 4: Verify build compiles (will fail until service constructor is updated)** + +Run: `cd api && go vet ./pkg/requests/... ./pkg/events/...` +Expected: No errors in these packages + +- [ ] **Step 5: Commit** + +```bash +cd api && git add -A && git commit -m "feat: add attachment fields to request, params, and event structs" +``` + +--- + +### Task 6: Add attachment validation + +**Files:** + +- Modify: `api/pkg/validators/message_handler_validator.go:49-77` + +- [ ] **Step 1: Update ValidateMessageReceive to validate attachments** + +In `api/pkg/validators/message_handler_validator.go`, replace the `ValidateMessageReceive` method (lines 49-77) with: + +```go +const ( + maxAttachmentCount = 10 + maxAttachmentSize = (3 * 1024 * 1024) / 2 // 1.5 MB +) + +// ValidateMessageReceive validates the requests.MessageReceive request +func (validator MessageHandlerValidator) ValidateMessageReceive(_ context.Context, request requests.MessageReceive) url.Values { + v := govalidator.New(govalidator.Options{ + Data: &request, + Rules: govalidator.MapData{ + "to": []string{ + "required", + phoneNumberRule, + }, + "from": []string{ + "required", + }, + "content": []string{ + "required", + "min:1", + "max:2048", + }, + "sim": []string{ + "required", + "in:" + strings.Join([]string{ + string(entities.SIM1), + string(entities.SIM2), + }, ","), + }, + }, + }) + + errors := v.ValidateStruct() + + if len(request.Attachments) > 0 { + attachmentErrors := validator.validateAttachments(request.Attachments) + for key, values := range attachmentErrors { + for _, value := range values { + errors.Add(key, value) + } + } + } + + return errors +} + +func (validator MessageHandlerValidator) validateAttachments(attachments []requests.MessageAttachment) url.Values { + errors := url.Values{} + allowedTypes := repositories.AllowedContentTypes() + + if len(attachments) > maxAttachmentCount { + errors.Add("attachments", fmt.Sprintf("attachment count [%d] exceeds maximum of [%d]", len(attachments), maxAttachmentCount)) + return errors + } + + for i, attachment := range attachments { + if !allowedTypes[attachment.ContentType] { + errors.Add("attachments", fmt.Sprintf("attachment [%d] has unsupported content type [%s]", i, attachment.ContentType)) + continue + } + + decoded, err := base64.StdEncoding.DecodeString(attachment.Content) + if err != nil { + errors.Add("attachments", fmt.Sprintf("attachment [%d] has invalid base64 content", i)) + continue + } + + if len(decoded) > maxAttachmentSize { + errors.Add("attachments", fmt.Sprintf("attachment [%d] size [%d] exceeds maximum of [%d] bytes", i, len(decoded), maxAttachmentSize)) + } + } + + return errors +} +``` + +Add these imports to the file: `"encoding/base64"`, `"github.com/NdoleStudio/httpsms/pkg/repositories"`. + +- [ ] **Step 2: Verify build** + +Run: `cd api && go vet ./pkg/validators/...` +Expected: No errors + +- [ ] **Step 3: Commit** + +```bash +cd api && git add -A && git commit -m "feat: add attachment count, size, and content-type validation" +``` + +--- + +### Task 7: Upload logic in MessageService.ReceiveMessage() + +**Files:** + +- Modify: `api/pkg/services/message_service.go:22-47` (struct + constructor) +- Modify: `api/pkg/services/message_service.go:302-337` (ReceiveMessage) +- Modify: `api/pkg/services/message_service.go:550-581` (storeReceivedMessage) + +- [ ] **Step 1: Add AttachmentStorage and apiBaseURL to MessageService** + +Update the `MessageService` struct (lines 22-30): + +```go +type MessageService struct { + service + logger telemetry.Logger + tracer telemetry.Tracer + eventDispatcher *EventDispatcher + phoneService *PhoneService + repository repositories.MessageRepository + attachmentStorage repositories.AttachmentStorage + apiBaseURL string +} +``` + +Update `NewMessageService` (lines 33-47) to accept the new parameters: + +```go +func NewMessageService( + logger telemetry.Logger, + tracer telemetry.Tracer, + repository repositories.MessageRepository, + eventDispatcher *EventDispatcher, + phoneService *PhoneService, + attachmentStorage repositories.AttachmentStorage, + apiBaseURL string, +) (s *MessageService) { + return &MessageService{ + logger: logger.WithService(fmt.Sprintf("%T", s)), + tracer: tracer, + repository: repository, + phoneService: phoneService, + eventDispatcher: eventDispatcher, + attachmentStorage: attachmentStorage, + apiBaseURL: apiBaseURL, + } +} +``` + +- [ ] **Step 2: Add the uploadAttachments helper method** + +Add this after `storeReceivedMessage`. Add imports: `"encoding/base64"`, `"golang.org/x/sync/errgroup"`: + +```go +func (service *MessageService) uploadAttachments(ctx context.Context, userID entities.UserID, messageID uuid.UUID, attachments []ServiceAttachment) ([]string, error) { + ctx, span := service.tracer.Start(ctx) + defer span.End() + + ctxLogger := service.tracer.CtxLogger(service.logger, span) + + g, gCtx := errgroup.WithContext(ctx) + urls := make([]string, len(attachments)) + paths := make([]string, len(attachments)) + + for i, attachment := range attachments { + i, attachment := i, attachment + g.Go(func() error { + decoded, err := base64.StdEncoding.DecodeString(attachment.Content) + if err != nil { + return stacktrace.Propagate(err, fmt.Sprintf("cannot decode base64 content for attachment [%d]", i)) + } + + sanitizedName := repositories.SanitizeFilename(attachment.Name, i) + ext := repositories.ExtensionFromContentType(attachment.ContentType) + filename := sanitizedName + ext + + path := fmt.Sprintf("attachments/%s/%s/%d/%s", userID, messageID, i, filename) + paths[i] = path + + if err = service.attachmentStorage.Upload(gCtx, path, decoded); err != nil { + return stacktrace.Propagate(err, fmt.Sprintf("cannot upload attachment [%d] to path [%s]", i, path)) + } + + urls[i] = fmt.Sprintf("%s/v1/attachments/%s/%s/%d/%s", service.apiBaseURL, userID, messageID, i, filename) + ctxLogger.Info(fmt.Sprintf("uploaded attachment [%d] to [%s]", i, path)) + return nil + }) + } + + if err := g.Wait(); err != nil { + for _, path := range paths { + if path != "" { + _ = service.attachmentStorage.Delete(ctx, path) + } + } + return nil, stacktrace.Propagate(err, "cannot upload attachments") + } + + return urls, nil +} +``` + +- [ ] **Step 3: Update ReceiveMessage to upload attachments before event dispatch** + +Replace the `ReceiveMessage` method (lines 302-337): + +```go +func (service *MessageService) ReceiveMessage(ctx context.Context, params *MessageReceiveParams) (*entities.Message, error) { + ctx, span := service.tracer.Start(ctx) + defer span.End() + + ctxLogger := service.tracer.CtxLogger(service.logger, span) + + messageID := uuid.New() + var attachmentURLs []string + + if len(params.Attachments) > 0 { + ctxLogger.Info(fmt.Sprintf("uploading [%d] attachments for message [%s]", len(params.Attachments), messageID)) + var err error + attachmentURLs, err = service.uploadAttachments(ctx, params.UserID, messageID, params.Attachments) + if err != nil { + msg := fmt.Sprintf("cannot upload attachments for message [%s]", messageID) + return nil, service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + } + + eventPayload := events.MessagePhoneReceivedPayload{ + MessageID: messageID, + UserID: params.UserID, + Encrypted: params.Encrypted, + Owner: phonenumbers.Format(¶ms.Owner, phonenumbers.E164), + Contact: params.Contact, + Timestamp: params.Timestamp, + Content: params.Content, + SIM: params.SIM, + Attachments: attachmentURLs, + } + + ctxLogger.Info(fmt.Sprintf("creating cloud event for received with ID [%s]", eventPayload.MessageID)) + + event, err := service.createMessagePhoneReceivedEvent(params.Source, eventPayload) + if err != nil { + msg := fmt.Sprintf("cannot create %T from payload with message id [%s]", event, eventPayload.MessageID) + return nil, service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + + ctxLogger.Info(fmt.Sprintf("created event [%s] with id [%s] and message id [%s]", event.Type(), event.ID(), eventPayload.MessageID)) + + if err = service.eventDispatcher.Dispatch(ctx, event); err != nil { + msg := fmt.Sprintf("cannot dispatch event type [%s] and id [%s]", event.Type(), event.ID()) + return nil, service.tracer.WrapErrorSpan(span, stacktrace.Propagate(err, msg)) + } + ctxLogger.Info(fmt.Sprintf("event [%s] dispatched successfully", event.ID())) + + return service.storeReceivedMessage(ctx, eventPayload) +} +``` + +- [ ] **Step 4: Update storeReceivedMessage to set Attachments on message** + +In the `storeReceivedMessage` method (lines 550-581), add `Attachments` to the message construction: + +```go + message := &entities.Message{ + ID: params.MessageID, + Owner: params.Owner, + UserID: params.UserID, + Contact: params.Contact, + Content: params.Content, + Attachments: params.Attachments, + SIM: params.SIM, + Encrypted: params.Encrypted, + Type: entities.MessageTypeMobileOriginated, + Status: entities.MessageStatusReceived, + RequestReceivedAt: params.Timestamp, + CreatedAt: time.Now().UTC(), + UpdatedAt: time.Now().UTC(), + OrderTimestamp: params.Timestamp, + ReceivedAt: ¶ms.Timestamp, + } +``` + +- [ ] **Step 5: Verify the services package compiles** + +Run: `cd api && go vet ./pkg/services/...` +Expected: No errors (the full build may still fail until DI container is updated) + +- [ ] **Step 6: Commit** + +```bash +cd api && git add -A && git commit -m "feat: add attachment upload logic to MessageService.ReceiveMessage()" +``` + +--- + +### Task 8: Attachment download handler + +**Files:** + +- Create: `api/pkg/handlers/attachment_handler.go` + +- [ ] **Step 1: Write the handler** + +Create `api/pkg/handlers/attachment_handler.go`: + +```go +package handlers + +import ( + "fmt" + "path/filepath" + + "github.com/NdoleStudio/httpsms/pkg/repositories" + "github.com/NdoleStudio/httpsms/pkg/telemetry" + "github.com/gofiber/fiber/v2" + "github.com/palantir/stacktrace" +) + +// AttachmentHandler handles attachment download requests +type AttachmentHandler struct { + handler + logger telemetry.Logger + tracer telemetry.Tracer + storage repositories.AttachmentStorage +} + +// NewAttachmentHandler creates a new AttachmentHandler +func NewAttachmentHandler( + logger telemetry.Logger, + tracer telemetry.Tracer, + storage repositories.AttachmentStorage, +) (h *AttachmentHandler) { + return &AttachmentHandler{ + logger: logger.WithService(fmt.Sprintf("%T", h)), + tracer: tracer, + storage: storage, + } +} + +// RegisterRoutes registers the routes for the AttachmentHandler (no auth middleware — public endpoint) +func (h *AttachmentHandler) RegisterRoutes(router fiber.Router) { + router.Get("/v1/attachments/:userID/:messageID/:attachmentIndex/:filename", h.GetAttachment) +} + +// GetAttachment downloads an attachment +// @Summary Download a message attachment +// @Description Download an MMS attachment by its path components +// @Tags Attachments +// @Produce octet-stream +// @Param userID path string true "User ID" +// @Param messageID path string true "Message ID" +// @Param attachmentIndex path string true "Attachment index" +// @Param filename path string true "Filename with extension" +// @Success 200 {file} binary +// @Failure 404 {object} responses.NotFoundResponse +// @Failure 500 {object} responses.InternalServerError +// @Router /attachments/{userID}/{messageID}/{attachmentIndex}/{filename} [get] +func (h *AttachmentHandler) GetAttachment(c *fiber.Ctx) error { + ctx, span := h.tracer.StartFromFiberCtx(c) + defer span.End() + + ctxLogger := h.tracer.CtxLogger(h.logger, span) + + userID := c.Params("userID") + messageID := c.Params("messageID") + attachmentIndex := c.Params("attachmentIndex") + filename := c.Params("filename") + + path := fmt.Sprintf("attachments/%s/%s/%s/%s", userID, messageID, attachmentIndex, filename) + + ctxLogger.Info(fmt.Sprintf("downloading attachment from path [%s]", path)) + + data, err := h.storage.Download(ctx, path) + if err != nil { + msg := fmt.Sprintf("cannot download attachment from path [%s]", path) + ctxLogger.Warn(stacktrace.Propagate(err, msg)) + return h.responseNotFound(c, "attachment not found") + } + + ext := filepath.Ext(filename) + contentType := repositories.ContentTypeFromExtension(ext) + + c.Set("Content-Type", contentType) + c.Set("Content-Disposition", "attachment") + c.Set("X-Content-Type-Options", "nosniff") + + return c.Send(data) +} +``` + +- [ ] **Step 2: Verify build** + +Run: `cd api && go vet ./pkg/handlers/...` +Expected: No errors + +- [ ] **Step 3: Commit** + +```bash +cd api && git add -A && git commit -m "feat: add AttachmentHandler for downloading attachments" +``` + +--- + +### Task 9: Wire everything in the DI container and env config + +**Files:** + +- Modify: `api/pkg/di/container.go:104-163` (NewContainer) +- Modify: `api/pkg/di/container.go:1424-1434` (MessageService creation) +- Modify: `api/.env.docker` + +- [ ] **Step 1: Add GCS_BUCKET_NAME to .env.docker** + +In `api/.env.docker`, add after the `REDIS_URL=redis://@redis:6379` line (line 49): + +```env + +# Google Cloud Storage bucket for MMS attachments. Leave empty to use in-memory storage. +GCS_BUCKET_NAME= +``` + +- [ ] **Step 2: Add `attachmentStorage` field to Container struct** + +In `api/pkg/di/container.go`, add `attachmentStorage` to the `Container` struct (around line 82-90): + +```go +type Container struct { + projectID string + db *gorm.DB + dedicatedDB *gorm.DB + version string + app *fiber.App + eventDispatcher *services.EventDispatcher + logger telemetry.Logger + attachmentStorage repositories.AttachmentStorage +} +``` + +- [ ] **Step 3: Add AttachmentStorage, APIBaseURL, and AttachmentHandler getters to container.go** + +Add these methods to `api/pkg/di/container.go`. Also add required imports: `"cloud.google.com/go/storage"` and `"context"`: + +```go +// AttachmentStorage creates a cached AttachmentStorage based on configuration +func (container *Container) AttachmentStorage() repositories.AttachmentStorage { + if container.attachmentStorage != nil { + return container.attachmentStorage + } + + bucket := os.Getenv("GCS_BUCKET_NAME") + if bucket != "" { + container.logger.Debug("creating GCSAttachmentStorage") + client, err := storage.NewClient(context.Background()) + if err != nil { + container.logger.Fatal(stacktrace.Propagate(err, "cannot create GCS client")) + } + container.attachmentStorage = repositories.NewGCSAttachmentStorage( + container.Logger(), + container.Tracer(), + client, + bucket, + ) + } else { + container.logger.Debug("creating MemoryAttachmentStorage (GCS_BUCKET_NAME not set)") + container.attachmentStorage = repositories.NewMemoryAttachmentStorage( + container.Logger(), + container.Tracer(), + ) + } + + return container.attachmentStorage +} + +// APIBaseURL returns the API base URL derived from EVENTS_QUEUE_ENDPOINT +func (container *Container) APIBaseURL() string { + endpoint := os.Getenv("EVENTS_QUEUE_ENDPOINT") + return strings.TrimSuffix(endpoint, "/v1/events") +} + +// AttachmentHandler creates a new AttachmentHandler +func (container *Container) AttachmentHandler() (handler *handlers.AttachmentHandler) { + container.logger.Debug(fmt.Sprintf("creating %T", handler)) + return handlers.NewAttachmentHandler( + container.Logger(), + container.Tracer(), + container.AttachmentStorage(), + ) +} + +// RegisterAttachmentRoutes registers routes for the /attachments prefix +func (container *Container) RegisterAttachmentRoutes() { + container.logger.Debug(fmt.Sprintf("registering %T routes", &handlers.AttachmentHandler{})) + container.AttachmentHandler().RegisterRoutes(container.App()) +} +``` + +- [ ] **Step 3: Update MessageService creation to pass new parameters** + +Update the `MessageService()` getter (around line 1424-1434): + +```go +func (container *Container) MessageService() (service *services.MessageService) { + container.logger.Debug(fmt.Sprintf("creating %T", service)) + return services.NewMessageService( + container.Logger(), + container.Tracer(), + container.MessageRepository(), + container.EventDispatcher(), + container.PhoneService(), + container.AttachmentStorage(), + container.APIBaseURL(), + ) +} +``` + +- [ ] **Step 4: Register attachment routes in NewContainer** + +In the `NewContainer` function (lines 104-163), add `container.RegisterAttachmentRoutes()` after `container.RegisterMessageRoutes()` (after line 120): + +```go + container.RegisterMessageRoutes() + container.RegisterAttachmentRoutes() + container.RegisterBulkMessageRoutes() +``` + +- [ ] **Step 5: Verify full build** + +Run: `cd api && go build ./...` +Expected: Build succeeds — all components are now wired + +- [ ] **Step 6: Run all tests** + +Run: `cd api && go test ./...` +Expected: All tests pass + +- [ ] **Step 7: Commit** + +```bash +cd api && git add -A && git commit -m "feat: wire attachment storage and handler in DI container + +- Add AttachmentStorage selection (GCS vs memory) based on GCS_BUCKET_NAME env var +- Wire AttachmentHandler for public download endpoint +- Pass storage and API base URL to MessageService +- Add GCS_BUCKET_NAME to .env.docker" +``` + +--- + +### Task 10: Final verification + +- [ ] **Step 1: Run full build** + +Run: `cd api && go build -o ./tmp/main.exe .` +Expected: Build succeeds + +- [ ] **Step 2: Run all tests** + +Run: `cd api && go test ./... -v` +Expected: All tests pass including `TestExtensionFromContentType` and `TestSanitizeFilename` + +- [ ] **Step 3: Verify go vet** + +Run: `cd api && go vet ./...` +Expected: No issues + +- [ ] **Step 4: Final commit if any remaining changes** + +```bash +cd api && git add -A && git diff --cached --stat +``` diff --git a/docs/superpowers/specs/2026-04-11-mms-attachments-design.md b/docs/superpowers/specs/2026-04-11-mms-attachments-design.md new file mode 100644 index 00000000..7beea85f --- /dev/null +++ b/docs/superpowers/specs/2026-04-11-mms-attachments-design.md @@ -0,0 +1,220 @@ +# MMS Attachment Support — Design Spec + +## Problem + +The Android app now forwards MMS attachments (as base64-encoded data) when receiving MMS messages via `HttpSmsApiService.receive()`. The API server needs to: + +1. Accept attachment data in the receive endpoint +2. Upload attachments to cloud storage (GCS or in-memory) +3. Store download URLs in the Message entity +4. Serve a download endpoint for retrieving attachments +5. Include attachment URLs in webhook event payloads + +## Approach + +**Approach A: Storage Interface + Minimal New Code** — Add an `AttachmentStorage` interface with GCS and memory implementations. Upload logic lives in the existing `MessageService.ReceiveMessage()` flow (synchronous). A new `AttachmentHandler` serves downloads. No new database tables — content type is encoded in the URL file extension. + +## Design + +### 1. Storage Interface + +**New file: `pkg/repositories/attachment_storage.go`** + +```go +type AttachmentStorage interface { + Upload(ctx context.Context, path string, data []byte) error + Download(ctx context.Context, path string) ([]byte, error) + Delete(ctx context.Context, path string) error +} +``` + +**GCS Implementation** (`pkg/repositories/gcs_attachment_storage.go`): + +- Uses `cloud.google.com/go/storage` SDK +- Configured with bucket name from `GCS_BUCKET_NAME` env var +- Stores objects at: `attachments/{userID}/{messageID}/{index}/{name}.{ext}` +- Extension derived from content type (e.g., `image/jpeg` → `.jpg`); falls back to `.bin` only when no mapping exists + +**Memory Implementation** (`pkg/repositories/memory_attachment_storage.go`): + +- `sync.Map`-backed in-memory store +- Used when `GCS_BUCKET_NAME` is empty/unset (local dev, testing) + +**DI selection** (in `container.go`): + +```go +if os.Getenv("GCS_BUCKET_NAME") != "" { + return NewGCSAttachmentStorage(bucket, tracer, logger) +} +return NewMemoryAttachmentStorage(tracer, logger) +``` + +### 2. Environment Variables + +| Variable | Description | Default | +| ----------------- | ------------------------------------------------------- | ---------------------------------- | +| `GCS_BUCKET_NAME` | GCS bucket for attachments. Empty = use memory storage. | `httpsms-86c51.appspot.com` (prod) | + +The API base URL for constructing download links is derived from `EVENTS_QUEUE_ENDPOINT` by stripping the `/v1/events` suffix. + +### 3. Request & Validation Changes + +**Updated `MessageReceive` request** (`pkg/requests/`): + +```go +type MessageReceive struct { + From string `json:"from"` + To string `json:"to"` + Content string `json:"content"` + Encrypted bool `json:"encrypted"` + SIM entities.SIM `json:"sim"` + Timestamp time.Time `json:"timestamp"` + Attachments []MessageAttachment `json:"attachments"` // NEW +} + +type MessageAttachment struct { + Name string `json:"name"` + ContentType string `json:"content_type"` + Content string `json:"content"` // base64-encoded +} +``` + +**Updated `MessageReceiveParams`** (`pkg/services/`): +The `ToMessageReceiveParams()` method must propagate attachments to the service layer: + +```go +type MessageReceiveParams struct { + // ... existing fields ... + Attachments []requests.MessageAttachment // NEW — raw attachment data for upload +} +``` + +**Filename sanitization:** +The `Name` field from the Android client must be sanitized to prevent path traversal attacks. Strip all path separators (`/`, `\`), directory traversal sequences (`..`), and non-printable characters. If the sanitized name is empty, use a fallback like `attachment-{index}`. + +**Content type allowlist:** +Only allow known-safe MIME types from the extension mapping table (Section 5). Reject attachments with unrecognized content types with a 400 error. + +**Validation rules** (in `pkg/validators/`): + +- Attachment count must be ≤ 10 +- Each decoded attachment must be ≤ 1.5 MB (1,572,864 bytes) +- Content type must be in the allowlist +- If any limit is exceeded → **reject entire request with 400 Bad Request** +- Validation happens before any upload or storage + +### 4. Upload Flow (Synchronous in Receive) + +In `MessageService.ReceiveMessage()`: + +1. Validate attachment count, sizes, and content types +2. Upload attachments **in parallel** using `errgroup`: + a. Decode base64 content + b. Sanitize `name` (strip path separators, `..`, non-printable chars; fallback to `attachment-{index}`) + c. Map `content_type` → file extension (e.g., `image/jpeg` → `.jpg`, unknown → `.bin`) + d. Upload to storage at path: `attachments/{userID}/{messageID}/{index}/{sanitizedName}.{ext}` + e. Build download URL: `{apiBaseURL}/v1/attachments/{userID}/{messageID}/{index}/{sanitizedName}.{ext}` +3. If any upload fails → best-effort delete of already-uploaded files, then return 500 +4. Collect download URLs into `message.Attachments` (existing `pq.StringArray` field) +5. Set `Attachments` on `MessagePhoneReceivedPayload` before dispatching event +6. `storeReceivedMessage()` copies `payload.Attachments` → `message.Attachments` +7. Store message in database +8. Fire `message.phone.received` event (includes attachment URLs) + +### 5. Content Type → Extension Mapping + +A utility function maps MIME types to file extensions: + +| Content Type | Extension | +| ----------------- | --------- | +| `image/jpeg` | `.jpg` | +| `image/png` | `.png` | +| `image/gif` | `.gif` | +| `image/webp` | `.webp` | +| `image/bmp` | `.bmp` | +| `video/mp4` | `.mp4` | +| `video/3gpp` | `.3gp` | +| `audio/mpeg` | `.mp3` | +| `audio/ogg` | `.ogg` | +| `audio/amr` | `.amr` | +| `application/pdf` | `.pdf` | +| `text/vcard` | `.vcf` | +| `text/x-vcard` | `.vcf` | +| _(default)_ | `.bin` | + +This covers common MMS content types. New mappings can be added as needed. + +### 6. Download Handler + +**New file: `pkg/handlers/attachment_handler.go`** + +**Route:** `GET /v1/attachments/:userID/:messageID/:attachmentIndex/:filename` + +- Registered **without authentication middleware** — publicly accessible, consistent with outgoing attachment URLs +- The `{userID}/{messageID}/{attachmentIndex}` path components provide sufficient obscurity (UUIDs are unguessable) + +**Download flow:** + +1. Parse URL params (userID, messageID, attachmentIndex, filename) +2. Construct storage path: `attachments/{userID}/{messageID}/{attachmentIndex}/{filename}` +3. Fetch bytes from `AttachmentStorage.Download(ctx, path)` +4. Derive `Content-Type` from filename extension +5. Set security headers: `Content-Disposition: attachment`, `X-Content-Type-Options: nosniff` +6. Respond with binary data + correct `Content-Type` header +7. Return 404 if attachment not found in storage + +### 7. Webhook Event Changes + +**Updated `MessagePhoneReceivedPayload`** (`pkg/events/message_phone_received_event.go`): + +```go +type MessagePhoneReceivedPayload struct { + MessageID uuid.UUID `json:"message_id"` + UserID entities.UserID `json:"user_id"` + Owner string `json:"owner"` + Encrypted bool `json:"encrypted"` + Contact string `json:"contact"` + Timestamp time.Time `json:"timestamp"` + Content string `json:"content"` + SIM entities.SIM `json:"sim"` + Attachments []string `json:"attachments"` // NEW — download URLs +} +``` + +Webhook subscribers will receive the array of download URLs. They can `GET` each URL directly — no authentication required. + +### 8. Files Changed / Created + +**New files:** + +- `pkg/repositories/attachment_storage.go` — Interface definition +- `pkg/repositories/gcs_attachment_storage.go` — GCS implementation +- `pkg/repositories/memory_attachment_storage.go` — Memory implementation +- `pkg/handlers/attachment_handler.go` — Download endpoint handler +- `pkg/validators/attachment_handler_validator.go` — Download param validation + +**Modified files:** + +- `pkg/requests/message_receive.go` (or wherever `MessageReceive` is defined) — Add `Attachments` field +- `pkg/validators/message_handler_validator.go` — Add attachment count/size validation +- `pkg/services/message_service.go` — Add upload logic to `ReceiveMessage()` +- `pkg/events/message_phone_received_event.go` — Add `Attachments` field to payload +- `pkg/di/container.go` — Wire storage, new handler, pass storage to message service +- `api/.env.docker` — Add `GCS_BUCKET_NAME` variable +- `go.mod` / `go.sum` — Add `cloud.google.com/go/storage` dependency + +### 9. Validation Constraints + +| Constraint | Value | Behavior | +| ------------------------------- | ------------------------ | ---------------------------------------------------- | +| Max attachment count | 10 | 400 Bad Request | +| Max attachment size (decoded) | 1.5 MB (1,572,864 bytes) | 400 Bad Request | +| Content type not in allowlist | — | 400 Bad Request | +| Missing/empty attachments array | — | Message stored without attachments (normal SMS flow) | + +### 10. Error Handling + +- Storage upload failure → Best-effort delete of already-uploaded attachments, then return 500; message is NOT stored +- Storage download failure → Return 404 or 500 depending on error type +- Invalid base64 content → Return 400 Bad Request +- All errors wrapped with `stacktrace.Propagate()` per project convention diff --git a/web/.dockerignore b/web/.dockerignore new file mode 100644 index 00000000..492c4b07 --- /dev/null +++ b/web/.dockerignore @@ -0,0 +1,3 @@ +node_modules +coverage +.nuxt diff --git a/web/.env.docker b/web/.env.docker new file mode 100644 index 00000000..b1751dfb --- /dev/null +++ b/web/.env.docker @@ -0,0 +1,21 @@ +API_BASE_URL=http://localhost:8000 + +APP_URL=http://localhost:3000 +APP_NAME=httpSMS +APP_GITHUB_URL=https://github.com/NdoleStudio/httpsms +APP_DOCUMENTATION_URL=https://docs.httpsms.com +APP_DOWNLOAD_URL=https://github.com/NdoleStudio/httpsms/releases/latest/download/HttpSms.apk +APP_ENV=production + +# Firebase credentials +FIREBASE_API_KEY=AIzaSyAKqPvj51igvvNNcRt_gL0A6cgx3ZB-kuQ +FIREBASE_AUTH_DOMAIN=httpsms-docker.firebaseapp.com +FIREBASE_PROJECT_ID=httpsms-docker +FIREBASE_STORAGE_BUCKET=httpsms-docker.appspot.com +FIREBASE_MESSAGING_SENDER_ID=668063041624 +FIREBASE_APP_ID=668063041624:web:29b9e3b7027965ba08a22d +FIREBASE_MEASUREMENT_ID=G-18VRYL22PZ + +# Cloudflare Turnstile site key for captcha on the search messages page +# Get your site key at https://developers.cloudflare.com/turnstile/get-started/ +CLOUDFLARE_TURNSTILE_SITE_KEY= diff --git a/web/.env.production b/web/.env.production index c6425bfb..30cbebd0 100644 --- a/web/.env.production +++ b/web/.env.production @@ -1,11 +1,24 @@ -BASE_URL=https://api.httpsms.com +API_BASE_URL=https://api.httpsms.com APP_URL=https://httpsms.com -APP_NAME="HTTP SMS" +APP_NAME=httpSMS APP_GITHUB_URL=https://github.com/NdoleStudio/httpsms APP_DOCUMENTATION_URL=https://docs.httpsms.com -APP_DOWNLOAD_URL=https://github.com/NdoleStudio/httpsms/releases/latest/download/HttpSms.apk +APP_DOWNLOAD_URL=https://apk.httpsms.com/HttpSms.apk APP_ENV=production CHECKOUT_URL=https://httpsms.lemonsqueezy.com/checkout/buy/706c5638-4c8d-40db-a6f2-b6371b7e0af4 ENTERPRISE_CHECKOUT_URL=https://httpsms.lemonsqueezy.com/checkout/buy/d107cf05-4b13-4ebd-a770-c2cc75c69a14 + +FIREBASE_API_KEY=AIzaSyClL8AX2H_F77_n8yu5FgLzBmJTiSM0NsQ +FIREBASE_AUTH_DOMAIN=httpsms-86c51.firebaseapp.com +FIREBASE_PROJECT_ID=httpsms-86c51 +FIREBASE_STORAGE_BUCKET=httpsms-86c51.appspot.com +FIREBASE_MESSAGING_SENDER_ID=877524083399 +FIREBASE_APP_ID=1:877524083399:web:430d6a29a0d808946514e2 +FIREBASE_MEASUREMENT_ID=G-EZ5W9DVK8T + +CLOUDFLARE_TURNSTILE_SITE_KEY=0x4AAAAAAA6Hpp8SDyMMPhWg + +PUSHER_KEY=a4809008d8f03aaab022 +PUSHER_CLUSTER=mt1 diff --git a/web/.prettierignore b/web/.prettierignore index 1ec6ab60..6cd79f5d 100644 --- a/web/.prettierignore +++ b/web/.prettierignore @@ -94,3 +94,8 @@ sw.* # Vim swap files *.swp + + +# package lock +pnpm-lock.yaml +package.json diff --git a/web/Dockerfile b/web/Dockerfile new file mode 100644 index 00000000..813399ea --- /dev/null +++ b/web/Dockerfile @@ -0,0 +1,27 @@ +# build stage +FROM node:lts-alpine AS build + +WORKDIR /app + +COPY package.json pnpm-lock.yaml ./ + +# Install pnpm +RUN npm install -g pnpm + +# install python +RUN apk add --no-cache python3 + +RUN pnpm install +COPY . . +RUN pnpm run generate + +# production stage +FROM nginx:stable-alpine as production +COPY --from=build /app/dist /usr/share/nginx/html + +# Copy the nginx configuration file +COPY ./nginx.conf /etc/nginx/conf.d/default.conf + +EXPOSE 3000 + +CMD ["nginx", "-g", "daemon off;"] diff --git a/web/README.md b/web/README.md deleted file mode 100644 index ea10896e..00000000 --- a/web/README.md +++ /dev/null @@ -1,74 +0,0 @@ -# HTTP SMS WEB APP - -[![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/ndolestudio/httpsms) - -## URL - -https://httpsms.com - -## Build Setup - -```bash -# install dependencies -$ yarn install - -# serve with hot reload at localhost:3000 -$ yarn dev - -# build for production and launch server -$ yarn build -$ yarn start - -# generate static project -$ yarn generate -``` - -For detailed explanation on how things work, check out the [documentation](https://nuxtjs.org). - -## Special Directories - -You can create the following extra directories, some of which have special behaviors. Only `pages` is required; you can delete them if you don't want to use their functionality. - -### `assets` - -The assets directory contains your uncompiled assets such as Stylus or Sass files, images, or fonts. - -More information about the usage of this directory in [the documentation](https://nuxtjs.org/docs/2.x/directory-structure/assets). - -### `components` - -The components directory contains your Vue.js components. Components make up the different parts of your page and can be reused and imported into your pages, layouts and even other components. - -More information about the usage of this directory in [the documentation](https://nuxtjs.org/docs/2.x/directory-structure/components). - -### `layouts` - -Layouts are a great help when you want to change the look and feel of your Nuxt app, whether you want to include a sidebar or have distinct layouts for mobile and desktop. - -More information about the usage of this directory in [the documentation](https://nuxtjs.org/docs/2.x/directory-structure/layouts). - -### `pages` - -This directory contains your application views and routes. Nuxt will read all the `*.vue` files inside this directory and setup Vue Router automatically. - -More information about the usage of this directory in [the documentation](https://nuxtjs.org/docs/2.x/get-started/routing). - -### `plugins` - -The plugins directory contains JavaScript plugins that you want to run before instantiating the root Vue.js Application. This is the place to add Vue plugins and to inject functions or constants. Every time you need to use `Vue.use()`, you should create a file in `plugins/` and add its path to plugins in `nuxt.config.js`. - -More information about the usage of this directory in [the documentation](https://nuxtjs.org/docs/2.x/directory-structure/plugins). - -### `static` - -This directory contains your static files. Each file inside this directory is mapped to `/`. - -Example: `/static/robots.txt` is mapped as `/robots.txt`. - -More information about the usage of this directory in [the documentation](https://nuxtjs.org/docs/2.x/directory-structure/static). - -### `store` - -This directory contains your Vuex store files. Creating a file in this directory automatically activates Vuex. - -More information about the usage of this directory in [the documentation](https://nuxtjs.org/docs/2.x/directory-structure/store). diff --git a/web/assets/img/logos/uneed.svg b/web/assets/img/logos/uneed.svg new file mode 100644 index 00000000..5bad7c77 --- /dev/null +++ b/web/assets/img/logos/uneed.svg @@ -0,0 +1 @@ + diff --git a/web/assets/img/manage-phones.svg b/web/assets/img/manage-phones.svg new file mode 100644 index 00000000..66af6ba5 --- /dev/null +++ b/web/assets/img/manage-phones.svg @@ -0,0 +1 @@ + diff --git a/web/assets/img/mobile-encryption.svg b/web/assets/img/mobile-encryption.svg new file mode 100644 index 00000000..2395101c --- /dev/null +++ b/web/assets/img/mobile-encryption.svg @@ -0,0 +1 @@ + diff --git a/web/assets/img/schedule-messages.svg b/web/assets/img/schedule-messages.svg new file mode 100644 index 00000000..5906b27e --- /dev/null +++ b/web/assets/img/schedule-messages.svg @@ -0,0 +1 @@ + diff --git a/web/components/BlogInfo.vue b/web/components/BlogInfo.vue index 43dac38e..b49b1a35 100644 --- a/web/components/BlogInfo.vue +++ b/web/components/BlogInfo.vue @@ -1,18 +1,11 @@ @@ -51,17 +32,7 @@ export default Vue.extend({ data() { return { mdiBookOpenVariant, - substackLoaded: false, } }, - mounted() { - setTimeout(() => { - const s = document.createElement('script') - s.type = 'text/javascript' - s.src = 'https://substackapi.com/widget.js' - document.getElementsByTagName('head')[0].appendChild(s) - this.substackLoaded = true - }, 5000) - }, }) diff --git a/web/components/CopyButton.vue b/web/components/CopyButton.vue index aa94a7e6..20a94a5f 100644 --- a/web/components/CopyButton.vue +++ b/web/components/CopyButton.vue @@ -7,7 +7,7 @@ :large="large" @click="copy" > - {{ mdiContentCopy }} + {{ mdiContentCopy }} {{ copyText }} diff --git a/web/components/FirebaseAuth.vue b/web/components/FirebaseAuth.vue index c1f30286..365cfcc8 100644 --- a/web/components/FirebaseAuth.vue +++ b/web/components/FirebaseAuth.vue @@ -78,8 +78,8 @@ export default class FirebaseAuth extends Vue { signInSuccessUrl: window.location.href, signInOptions: [ // Leave the lines as is for the providers you want to offer your users. - ProviderId.GITHUB, ProviderId.GOOGLE, + ProviderId.GITHUB, ProviderId.PASSWORD, ], // Terms of service url. diff --git a/web/components/MessageThread.vue b/web/components/MessageThread.vue index 0f0f222b..51676b85 100644 --- a/web/components/MessageThread.vue +++ b/web/components/MessageThread.vue @@ -95,8 +95,10 @@ {{ thread.contact | phoneNumber }} - - {{ thread.last_message_content }} + + + {{ thread.last_message_content }} + @@ -150,6 +152,7 @@ import { mdiCheck, mdiAlert, mdiAccount, + mdiPaperclip, } from '@mdi/js' @Component @@ -160,6 +163,7 @@ export default class MessageThread extends Vue { mdiAlert = mdiAlert mdiCheck = mdiCheck mdiCheckAll = mdiCheckAll + mdiPaperclip = mdiPaperclip get threads(): Array { return this.$store.getters.getThreads diff --git a/web/components/MessageThreadHeader.vue b/web/components/MessageThreadHeader.vue index 2086354d..6cbee07a 100644 --- a/web/components/MessageThreadHeader.vue +++ b/web/components/MessageThreadHeader.vue @@ -135,6 +135,22 @@ + + + {{ mdiMagnify }} + + + + + Search Messages + + + + {{ mdiAccountCog }} @@ -147,6 +163,18 @@ + + + {{ mdiCellphoneKey }} + + + + + Phone API Keys + + + + { return this.$store.getters.getPhones.map( diff --git a/web/layouts/default.vue b/web/layouts/default.vue index 0be46745..cff2bd34 100644 --- a/web/layouts/default.vue +++ b/web/layouts/default.vue @@ -28,26 +28,31 @@ @@ -129,4 +127,19 @@ export default class DefaultLayout extends Vue { font-size: 16px; } } + +.feedback-btn { + position: fixed; + z-index: 15; + right: -56px; + margin: 0; + top: 45%; + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; + transform: rotate(-90deg); + -moz-transform: rotate(-90deg); + -ms-transform: rotate(-90deg); + -o-transform: rotate(-90deg); + -webkit-transform: rotate(-90deg); +} diff --git a/web/layouts/website.vue b/web/layouts/website.vue index 48dede94..00bd0ed5 100644 --- a/web/layouts/website.vue +++ b/web/layouts/website.vue @@ -4,7 +4,11 @@ - + @@ -17,7 +21,7 @@ Get Started -  For Free +  For Free + + + Dashboard @@ -74,7 +98,7 @@

httpSMS

-

+

Made With {{ mdiHeart }} in Tallinn -

+

{{ mdiTwitter }} @@ -109,6 +133,16 @@ >

+ + httpSMS badge +

Resources

@@ -202,6 +236,18 @@ +
  • + + + Request Feature + {{ mdiLightbulbOn50 }} + + +
  • @@ -261,7 +307,9 @@ import { mdiTwitter, mdiHeart, mdiShieldStar, + mdiLightbulbOn50, mdiCreation, + mdiDomain, mdiEyeOffOutline, mdiPost, mdiCreditCardOutline, @@ -285,8 +333,10 @@ export default Vue.extend({ mdiCreditCardOutline, mdiEmailOutline, mdiPost, + mdiDomain, mdiCircle, mdiShieldStar, + mdiLightbulbOn50, mdiBookOpenVariant, } }, diff --git a/web/models/api.ts b/web/models/api.ts index ba28450c..660e7493 100644 --- a/web/models/api.ts +++ b/web/models/api.ts @@ -1,5 +1,4 @@ -/* eslint-disable */ -/* tslint:disable */ +// @ts-nocheck /* * --------------------------------------------------------------- * ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ## @@ -48,6 +47,8 @@ export interface EntitiesDiscord { } export interface EntitiesHeartbeat { + /** @example true */ + charging: boolean /** @example "32343a19-da5e-4b1b-a767-3298a73703cb" */ id: string /** @example "+18005550199" */ @@ -61,8 +62,6 @@ export interface EntitiesHeartbeat { } export interface EntitiesMessage { - /** @example false */ - can_be_polled: boolean /** @example "+18005550100" */ contact: string /** @example "This is a sample text message" */ @@ -70,17 +69,19 @@ export interface EntitiesMessage { /** @example "2022-06-05T14:26:02.302718+03:00" */ created_at: string /** @example "2022-06-05T14:26:09.527976+03:00" */ - delivered_at: string + delivered_at?: string + /** @example false */ + encrypted: boolean /** @example "2022-06-05T14:26:09.527976+03:00" */ - expired_at: string + expired_at?: string /** @example "2022-06-05T14:26:09.527976+03:00" */ - failed_at: string + failed_at?: string /** @example "UNKNOWN" */ - failure_reason: string + failure_reason?: string /** @example "32343a19-da5e-4b1b-a767-3298a73703cb" */ id: string /** @example "2022-06-05T14:26:09.527976+03:00" */ - last_attempted_at: string + last_attempted_at?: string /** @example 1 */ max_send_attempts: number /** @example "2022-06-05T14:26:09.527976+03:00" */ @@ -88,22 +89,24 @@ export interface EntitiesMessage { /** @example "+18005550199" */ owner: string /** @example "2022-06-05T14:26:09.527976+03:00" */ - received_at: string + received_at?: string /** @example "153554b5-ae44-44a0-8f4f-7bbac5657ad4" */ - request_id: string + request_id?: string /** @example "2022-06-05T14:26:01.520828+03:00" */ request_received_at: string /** @example "2022-06-05T14:26:09.527976+03:00" */ - scheduled_at: string + scheduled_at?: string + /** @example "2022-06-05T14:26:09.527976+03:00" */ + scheduled_send_time?: string /** @example 0 */ send_attempt_count: number /** * SendDuration is the number of nanoseconds from when the request was received until when the mobile phone send the message * @example 133414 */ - send_time: number + send_time?: number /** @example "2022-06-05T14:26:09.527976+03:00" */ - sent_at: string + sent_at?: string /** * SIM is the SIM card to use to send the message * * SMS1: use the SIM card in slot 1 @@ -111,7 +114,7 @@ export interface EntitiesMessage { * * DEFAULT: used the default communication SIM card * @example "DEFAULT" */ - sim: EntitiesSIM + sim: string /** @example "pending" */ status: string /** @example "mobile-terminated" */ @@ -153,7 +156,7 @@ export interface EntitiesPhone { /** @example "2022-06-05T14:26:02.302718+03:00" */ created_at: string /** @example "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzd....." */ - fcm_token: string + fcm_token?: string /** @example "32343a19-da5e-4b1b-a767-3298a73703cb" */ id: string /** @@ -165,35 +168,43 @@ export interface EntitiesPhone { message_expiration_seconds: number /** @example 1 */ messages_per_minute: number + /** @example "This phone cannot receive calls. Please send an SMS instead." */ + missed_call_auto_reply?: string /** @example "+18005550199" */ phone_number: string - sim: EntitiesSIM + /** SIM card that received the message */ + sim: string /** @example "2022-06-05T14:26:10.303278+03:00" */ updated_at: string /** @example "WB7DRDWrJZRGbYrv2CKGkqbzvqdC" */ user_id: string } -export enum EntitiesSIM { - SIM1 = 'SIM1', - SIM2 = 'SIM2', -} - -export enum EntitiesSubscriptionName { - SubscriptionNameFree = 'free', - SubscriptionNameProMonthly = 'pro-monthly', - SubscriptionNameProYearly = 'pro-yearly', - SubscriptionNameUltraMonthly = 'ultra-monthly', - SubscriptionNameUltraYearly = 'ultra-yearly', - SubscriptionNameProLifetime = 'pro-lifetime', - SubscriptionName20KMonthly = '20k-monthly', - SubscriptionName20KYearly = '20k-yearly', +export interface EntitiesPhoneAPIKey { + /** @example "pk_DGW8NwQp7mxKaSZ72Xq9v6xxxxx" */ + api_key: string + /** @example "2022-06-05T14:26:02.302718+03:00" */ + created_at: string + /** @example "32343a19-da5e-4b1b-a767-3298a73703cb" */ + id: string + /** @example "Business Phone Key" */ + name: string + /** @example ["32343a19-da5e-4b1b-a767-3298a73703cb","32343a19-da5e-4b1b-a767-3298a73703cc"] */ + phone_ids: string[] + /** @example ["+18005550199","+18005550100"] */ + phone_numbers: string[] + /** @example "2022-06-05T14:26:02.302718+03:00" */ + updated_at: string + /** @example "user@gmail.com" */ + user_email: string + /** @example "WB7DRDWrJZRGbYrv2CKGkqbzvqdC" */ + user_id: string } export interface EntitiesUser { /** @example "32343a19-da5e-4b1b-a767-3298a73703cb" */ - active_phone_id: string - /** @example "xyz" */ + active_phone_id?: string + /** @example "x-api-key" */ api_key: string /** @example "2022-06-05T14:26:02.302718+03:00" */ created_at: string @@ -206,17 +217,19 @@ export interface EntitiesUser { /** @example true */ notification_message_status_enabled: boolean /** @example true */ + notification_newsletter_enabled: boolean + /** @example true */ notification_webhook_enabled: boolean /** @example "2022-06-05T14:26:02.302718+03:00" */ - subscription_ends_at: string + subscription_ends_at?: string /** @example "8f9c71b8-b84e-4417-8408-a62274f65a08" */ subscription_id: string /** @example "free" */ - subscription_name: EntitiesSubscriptionName + subscription_name: string /** @example "2022-06-05T14:26:02.302718+03:00" */ - subscription_renews_at: string + subscription_renews_at?: string /** @example "on_trial" */ - subscription_status: string + subscription_status?: string /** @example "Europe/Helsinki" */ timezone: string /** @example "2022-06-05T14:26:10.303278+03:00" */ @@ -226,11 +239,11 @@ export interface EntitiesUser { export interface EntitiesWebhook { /** @example "2022-06-05T14:26:02.302718+03:00" */ created_at: string - /** @example ["[message.phone.received]"] */ + /** @example ["message.phone.received"] */ events: string[] /** @example "32343a19-da5e-4b1b-a767-3298a73703cb" */ id: string - /** @example ["[+18005550199","+18005550100]"] */ + /** @example ["+18005550199","+18005550100"] */ phone_numbers: string[] /** @example "DGW8NwQp7mxKaSZ72Xq9v67SLqSbWQvckzzmK8D6rvd7NywSEkdMJtuxKyEkYnCY" */ signing_key: string @@ -255,12 +268,18 @@ export interface RequestsDiscordUpdate { } export interface RequestsHeartbeatStore { - owner: string + charging: boolean + phone_numbers: string[] } export interface RequestsMessageBulkSend { /** @example "This is a sample text message" */ content: string + /** + * Encrypted is used to determine if the content is end-to-end encrypted. Make sure to set the encryption key on the httpSMS mobile app + * @example false + */ + encrypted: boolean /** @example "+18005550199" */ from: string /** @@ -272,6 +291,17 @@ export interface RequestsMessageBulkSend { to: string[] } +export interface RequestsMessageCallMissed { + /** @example "+18005550199" */ + from: string + /** @example "SIM1" */ + sim: string + /** @example "2022-06-05T14:26:09.527976+03:00" */ + timestamp: string + /** @example "+18005550100" */ + to: string +} + export interface RequestsMessageEvent { /** * EventName is the type of event @@ -293,13 +323,18 @@ export interface RequestsMessageEvent { export interface RequestsMessageReceive { /** @example "This is a sample text message received on a phone" */ content: string + /** + * Encrypted is used to determine if the content is end-to-end encrypted. Make sure to set the encryption key on the httpSMS mobile app + * @example false + */ + encrypted: boolean /** @example "+18005550199" */ from: string /** * SIM card that received the message * @example "SIM1" */ - sim: EntitiesSIM + sim: string /** * Timestamp is the time when the event was emitted, Please send the timestamp in UTC with as much precision as possible * @example "2022-06-05T14:26:09.527976+03:00" @@ -312,6 +347,11 @@ export interface RequestsMessageReceive { export interface RequestsMessageSend { /** @example "This is a sample text message" */ content: string + /** + * Encrypted is an optional parameter used to determine if the content is end-to-end encrypted. Make sure to set the encryption key on the httpSMS mobile app + * @example false + */ + encrypted?: boolean /** @example "+18005550199" */ from: string /** @@ -319,6 +359,11 @@ export interface RequestsMessageSend { * @example "153554b5-ae44-44a0-8f4f-7bbac5657ad4" */ request_id?: string + /** + * SendAt is an optional parameter used to schedule a message to be sent in the future. The time is considered to be in your profile's local timezone and you can queue messages for up to 20 days (480 hours) in the future. + * @example "2025-12-19T16:39:57-08:00" + */ + send_at?: string /** @example "+18005550100" */ to: string } @@ -328,6 +373,23 @@ export interface RequestsMessageThreadUpdate { is_archived: boolean } +export interface RequestsPhoneAPIKeyStoreRequest { + /** @example "My Phone API Key" */ + name: string +} + +export interface RequestsPhoneFCMToken { + /** @example "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzd....." */ + fcm_token: string + /** @example "[+18005550199]" */ + phone_number: string + /** + * SIM is the SIM slot of the phone in case the phone has more than 1 SIM slot + * @example "SIM1" + */ + sim: string +} + export interface RequestsPhoneUpsert { /** @example "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzd....." */ fcm_token: string @@ -343,6 +405,8 @@ export interface RequestsPhoneUpsert { message_expiration_seconds: number /** @example 1 */ messages_per_minute: number + /** @example "e.g. This phone cannot receive calls. Please send an SMS instead." */ + missed_call_auto_reply: string /** @example "+18005550199" */ phone_number: string /** @@ -358,9 +422,28 @@ export interface RequestsUserNotificationUpdate { /** @example true */ message_status_enabled: boolean /** @example true */ + newsletter_enabled: boolean + /** @example true */ webhook_enabled: boolean } +export interface RequestsUserPaymentInvoice { + /** @example "221B Baker Street, London" */ + address: string + /** @example "Los Angeles" */ + city: string + /** @example "US" */ + country: string + /** @example "Acme Corp" */ + name: string + /** @example "Thank you for your business!" */ + notes: string + /** @example "CA" */ + state: string + /** @example "9800" */ + zip_code: string +} + export interface RequestsUserUpdate { /** @example "32343a19-da5e-4b1b-a767-3298a73703cb" */ active_phone_id: string @@ -395,7 +478,7 @@ export interface ResponsesBadRequest { export interface ResponsesBillingUsageResponse { data: EntitiesBillingUsage - /** @example "item created successfully" */ + /** @example "Request handled successfully" */ message: string /** @example "success" */ status: string @@ -403,7 +486,7 @@ export interface ResponsesBillingUsageResponse { export interface ResponsesBillingUsagesResponse { data: EntitiesBillingUsage[] - /** @example "item created successfully" */ + /** @example "Request handled successfully" */ message: string /** @example "success" */ status: string @@ -411,7 +494,7 @@ export interface ResponsesBillingUsagesResponse { export interface ResponsesDiscordResponse { data: EntitiesDiscord - /** @example "item created successfully" */ + /** @example "Request handled successfully" */ message: string /** @example "success" */ status: string @@ -419,7 +502,7 @@ export interface ResponsesDiscordResponse { export interface ResponsesDiscordsResponse { data: EntitiesDiscord[] - /** @example "item created successfully" */ + /** @example "Request handled successfully" */ message: string /** @example "success" */ status: string @@ -427,7 +510,7 @@ export interface ResponsesDiscordsResponse { export interface ResponsesHeartbeatResponse { data: EntitiesHeartbeat - /** @example "item created successfully" */ + /** @example "Request handled successfully" */ message: string /** @example "success" */ status: string @@ -435,7 +518,7 @@ export interface ResponsesHeartbeatResponse { export interface ResponsesHeartbeatsResponse { data: EntitiesHeartbeat[] - /** @example "item created successfully" */ + /** @example "Request handled successfully" */ message: string /** @example "success" */ status: string @@ -450,7 +533,7 @@ export interface ResponsesInternalServerError { export interface ResponsesMessageResponse { data: EntitiesMessage - /** @example "item created successfully" */ + /** @example "Request handled successfully" */ message: string /** @example "success" */ status: string @@ -458,7 +541,7 @@ export interface ResponsesMessageResponse { export interface ResponsesMessageThreadsResponse { data: EntitiesMessageThread[] - /** @example "item created successfully" */ + /** @example "Request handled successfully" */ message: string /** @example "success" */ status: string @@ -466,7 +549,7 @@ export interface ResponsesMessageThreadsResponse { export interface ResponsesMessagesResponse { data: EntitiesMessage[] - /** @example "item created successfully" */ + /** @example "Request handled successfully" */ message: string /** @example "success" */ status: string @@ -494,9 +577,25 @@ export interface ResponsesOkString { status: string } +export interface ResponsesPhoneAPIKeyResponse { + data: EntitiesPhoneAPIKey + /** @example "Request handled successfully" */ + message: string + /** @example "success" */ + status: string +} + +export interface ResponsesPhoneAPIKeysResponse { + data: EntitiesPhoneAPIKey[] + /** @example "Request handled successfully" */ + message: string + /** @example "success" */ + status: string +} + export interface ResponsesPhoneResponse { data: EntitiesPhone - /** @example "item created successfully" */ + /** @example "Request handled successfully" */ message: string /** @example "success" */ status: string @@ -504,7 +603,7 @@ export interface ResponsesPhoneResponse { export interface ResponsesPhonesResponse { data: EntitiesPhone[] - /** @example "item created successfully" */ + /** @example "Request handled successfully" */ message: string /** @example "success" */ status: string @@ -521,7 +620,7 @@ export interface ResponsesUnauthorized { export interface ResponsesUnprocessableEntity { data: Record - /** @example "validation errors while sending message" */ + /** @example "validation errors while handling request" */ message: string /** @example "error" */ status: string @@ -529,7 +628,47 @@ export interface ResponsesUnprocessableEntity { export interface ResponsesUserResponse { data: EntitiesUser - /** @example "item created successfully" */ + /** @example "Request handled successfully" */ + message: string + /** @example "success" */ + status: string +} + +export interface ResponsesUserSubscriptionPaymentsResponse { + data: { + attributes: { + billing_reason: string + card_brand: string + card_last_four: string + created_at: string + currency: string + currency_rate: string + discount_total: number + discount_total_formatted: string + discount_total_usd: number + refunded: boolean + refunded_amount: number + refunded_amount_formatted: string + refunded_amount_usd: number + refunded_at: any + status: string + status_formatted: string + subtotal: number + subtotal_formatted: string + subtotal_usd: number + tax: number + tax_formatted: string + tax_inclusive: boolean + tax_usd: number + total: number + total_formatted: string + total_usd: number + updated_at: string + } + id: string + type: string + }[] + /** @example "Request handled successfully" */ message: string /** @example "success" */ status: string @@ -537,7 +676,7 @@ export interface ResponsesUserResponse { export interface ResponsesWebhookResponse { data: EntitiesWebhook - /** @example "item created successfully" */ + /** @example "Request handled successfully" */ message: string /** @example "success" */ status: string @@ -545,7 +684,7 @@ export interface ResponsesWebhookResponse { export interface ResponsesWebhooksResponse { data: EntitiesWebhook[] - /** @example "item created successfully" */ + /** @example "Request handled successfully" */ message: string /** @example "success" */ status: string diff --git a/web/models/message.ts b/web/models/message.ts index b8948f93..b17b53ec 100644 --- a/web/models/message.ts +++ b/web/models/message.ts @@ -1,6 +1,7 @@ export interface Message { contact: string content: string + attachments: Array | null created_at: string failure_reason: string id: string @@ -15,3 +16,15 @@ export interface Message { type: string updated_at: string } + +export interface SearchMessagesRequest { + owners: string[] + types: string[] + statuses: string[] + query: string + sort_by: string + token?: string + sort_descending: boolean + skip: number + limit: number +} diff --git a/web/models/user.ts b/web/models/user.ts index 022144fc..8e615deb 100644 --- a/web/models/user.ts +++ b/web/models/user.ts @@ -9,7 +9,7 @@ export interface User { /** @example "free" */ subscription_name: string /** @example "2022-06-05T14:26:02.302718+03:00" */ - subscription_renews_at: string + subscription_renews_at: string | null /** @example "on_trial" */ subscription_status: string created_at: string diff --git a/web/nginx.conf b/web/nginx.conf new file mode 100644 index 00000000..979fe802 --- /dev/null +++ b/web/nginx.conf @@ -0,0 +1,9 @@ +server { + listen 3000; + server_name localhost; + root /usr/share/nginx/html; + index index.html index.htm; +location / { + try_files $uri $uri/ /index.html; + } +} diff --git a/web/nuxt.config.js b/web/nuxt.config.js index bd28a0ff..36ba81b9 100644 --- a/web/nuxt.config.js +++ b/web/nuxt.config.js @@ -22,6 +22,10 @@ export default { async: true, defer: true, }, + { + hid: 'cloudflare', + src: 'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit', + }, ], meta: [ { charset: 'utf-8' }, @@ -64,7 +68,7 @@ export default { // Plugins to run before rendering page: https://go.nuxtjs.dev/config-plugins plugins: [ - '~plugins/filters.ts', + '~/plugins/filters.ts', { src: '~/plugins/vue-glow', ssr: false }, { src: '~/plugins/chart', ssr: false }, ], @@ -90,13 +94,13 @@ export default { '@nuxtjs/firebase', { config: { - apiKey: 'AIzaSyClL8AX2H_F77_n8yu5FgLzBmJTiSM0NsQ', - authDomain: 'httpsms-86c51.firebaseapp.com', - projectId: 'httpsms-86c51', - storageBucket: 'httpsms-86c51.appspot.com', - messagingSenderId: '877524083399', - appId: '1:877524083399:web:430d6a29a0d808946514e2', - measurementId: 'G-EZ5W9DVK8T', + apiKey: process.env.FIREBASE_API_KEY, + authDomain: process.env.FIREBASE_AUTH_DOMAIN, + projectId: process.env.FIREBASE_PROJECT_ID, + storageBucket: process.env.FIREBASE_STORAGE_BUCKET, + messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID, + appId: process.env.FIREBASE_APP_ID, + measurementId: process.env.FIREBASE_MEASUREMENT_ID, }, services: { analytics: true, @@ -138,12 +142,21 @@ export default { hostname: 'https://httpsms.com', gzip: true, trailingSlash: true, - exclude: ['/messages', '/settings', '/threads**', '/billing'], + exclude: [ + '/messages', + '/settings', + '/threads**', + '/billing', + '/bulk-messages', + ], }, publicRuntimeConfig: { checkoutURL: process.env.CHECKOUT_URL, enterpriseCheckoutURL: process.env.ENTERPRISE_CHECKOUT_URL, + cloudflareTurnstileSiteKey: process.env.CLOUDFLARE_TURNSTILE_SITE_KEY, + pusherKey: process.env.PUSHER_KEY, + pusherCluster: process.env.PUSHER_CLUSTER, }, // Build Configuration: https://go.nuxtjs.dev/config-build diff --git a/web/package.json b/web/package.json index 2eac5d2a..9eb11649 100644 --- a/web/package.json +++ b/web/package.json @@ -2,6 +2,7 @@ "name": "web", "version": "1.0.0", "private": true, + "license": "AGPL-3.0-only", "scripts": { "dev": "nuxt", "build": "nuxt build", @@ -12,7 +13,7 @@ "lint:prettier": "prettier --check .", "lint": "yarn lint:js && yarn lint:style && yarn lint:prettier", "lintfix": "prettier --write --list-different . && yarn lint:js --fix && yarn lint:style --fix", - "api:models": "npx swagger-typescript-api -p ..\\api\\docs\\swagger.json -o ./models -n api.ts --no-client", + "api:models": "npx swagger-typescript-api generate -p ..\\api\\docs\\swagger.json -o ./models -n api.ts --no-client", "test": "jest" }, "lint-staged": { @@ -21,65 +22,69 @@ "*.**": "prettier --check --ignore-unknown" }, "dependencies": { - "@mdi/js": "^7.3.67", - "@nuxtjs/dotenv": "^1.4.1", + "@mdi/js": "^7.4.47", + "@nuxtjs/dotenv": "^1.4.2", "@nuxtjs/firebase": "^8.2.2", "@nuxtjs/sitemap": "^2.4.0", - "chart.js": "^4.4.0", + "chart.js": "^4.5.1", "chartjs-adapter-moment": "^1.0.1", - "core-js": "^3.33.2", + "core-js": "^3.48.0", "date-fns": "^2.30.0", - "dotenv": "^16.3.1", - "firebase": "^9.23.0", + "dotenv": "^17.2.3", + "firebase": "^10.14.1", "firebaseui": "^6.1.0", - "libphonenumber-js": "^1.10.49", - "moment": "^2.29.4", - "nuxt": "^2.17.2", + "jest-environment-jsdom": "^30.2.0", + "libphonenumber-js": "^1.12.36", + "moment": "^2.30.1", + "nuxt": "^2.18.1", "nuxt-highlightjs": "^1.0.3", - "ufo": "^1.1.2", - "vue": "^2.7.15", - "vue-chartjs": "^5.2.0", + "pusher-js": "^8.4.0", + "qrcode": "^1.5.0", + "ufo": "^1.6.1", + "vue": "^2.7.16", + "vue-chartjs": "^5.3.3", "vue-class-component": "^7.2.6", "vue-glow": "^1.4.2", "vue-property-decorator": "^9.1.2", "vue-router": "^3.6.5", - "vue-server-renderer": "2.7.14", - "vue-template-compiler": "^2.7.15", - "vuetify": "^2.7.1", + "vue-server-renderer": "2.7.16", + "vue-template-compiler": "^2.7.16", + "vuetify": "^2.7.2", "vuex": "^3.6.2", - "webpack": "^5.87.0" + "webpack": "^5.104.1" }, "devDependencies": { - "@babel/eslint-parser": "^7.22.15", - "@commitlint/cli": "^17.7.2", - "@commitlint/config-conventional": "^18.1.0", - "@nuxt/types": "^2.17.0", - "@nuxt/typescript-build": "^2.1.0", + "@babel/eslint-parser": "^7.28.6", + "@commitlint/cli": "^20.4.0", + "@commitlint/config-conventional": "^20.4.0", + "@nuxt/types": "^2.18.1", + "@nuxt/typescript-build": "^3.0.2", "@nuxtjs/eslint-config-typescript": "^12.1.0", "@nuxtjs/eslint-module": "^4.1.0", - "@nuxtjs/stylelint-module": "^5.1.0", + "@nuxtjs/stylelint-module": "^5.2.0", "@nuxtjs/vuetify": "^1.12.3", + "@types/qrcode": "^1.5.6", "@vue/test-utils": "^1.3.6", - "axios": "^0.25.0", + "axios": "^0.31.0", "babel-core": "7.0.0-bridge.0", - "babel-jest": "^29.7.0", - "eslint": "^8.53.0", - "eslint-config-prettier": "^9.0.0", + "babel-jest": "^30.2.0", + "eslint": "^8.57.1", + "eslint-config-prettier": "^10.1.8", "eslint-plugin-nuxt": "^4.0.0", - "eslint-plugin-vue": "^9.16.1", - "highlight.js": "^11.8.0", - "jest": "^27.4.4", - "lint-staged": "^14.0.1", - "node-fetch-native": "^1.4.1", - "postcss-html": "^1.5.0", - "prettier": "3.0.3", + "eslint-plugin-vue": "^9.33.0", + "highlight.js": "^11.11.1", + "jest": "^30.2.0", + "lint-staged": "^16.1.4", + "node-fetch-native": "^1.6.7", + "postcss-html": "^1.8.1", + "prettier": "3.8.1", "stylelint": "^15.11.0", "stylelint-config-prettier": "^9.0.5", "stylelint-config-recommended-vue": "^1.5.0", "stylelint-config-standard": "^34.0.0", - "ts-jest": "^27.1.1", + "ts-jest": "^29.4.6", "vue-client-only": "^2.1.0", - "vue-jest": "^3.0.4", + "vue-jest": "^3.0.7", "vue-meta": "^2.4.0", "vue-no-ssr": "^1.1.1" } diff --git a/web/pages/billing/index.vue b/web/pages/billing/index.vue index 5e914061..039f129f 100644 --- a/web/pages/billing/index.vue +++ b/web/pages/billing/index.vue @@ -83,7 +83,7 @@ :loading="loading" @click="updateDetails" > - Update Details + Update Plan -
    Overview
    +
    Overview

    This is the summary of the sent messages and received messages in -

    Usage History
    + +
    Usage History

    Summary of all the sent and received messages in the past 12 months @@ -337,6 +417,150 @@ + + + Generate Invoice + + Create an invoice for your + {{ selectedPayment?.attributes.total_formatted }} payment on + {{ selectedPayment?.attributes.created_at | timestamp }} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{ mdiDownloadOutline }} + Download Invoice + + + + Close + + + + @@ -347,7 +571,12 @@ import { mdiAccountCircle, mdiShieldCheck, mdiDelete, + mdiDownloadOutline, + mdiCog, mdiContentSave, + mdiCheck, + mdiAlert, + mdiInvoice, mdiEye, mdiEyeOff, mdiCallReceived, @@ -355,6 +584,11 @@ import { mdiCreditCard, mdiSquareEditOutline, } from '@mdi/js' +import { + RequestsUserPaymentInvoice, + ResponsesUserSubscriptionPaymentsResponse, +} from '~/models/api' +import { ErrorMessages } from '~/plugins/errors' type PaymentPlan = { name: string @@ -363,6 +597,14 @@ type PaymentPlan = { messagesPerMonth: number } +type subscriptionPayment = { + attributes: { + created_at: string + total_formatted: string + } + id: string +} + export default Vue.extend({ name: 'BillingIndex', middleware: ['auth'], @@ -371,16 +613,281 @@ export default Vue.extend({ mdiEye, mdiEyeOff, mdiArrowLeft, + mdiDownloadOutline, mdiAccountCircle, + mdiCheck, + mdiAlert, + mdiInvoice, mdiShieldCheck, mdiDelete, + mdiCog, mdiContentSave, mdiCallReceived, mdiCallMade, mdiCreditCard, mdiSquareEditOutline, loading: true, + loadingSubscriptionPayments: false, dialog: false, + payments: null as ResponsesUserSubscriptionPaymentsResponse | null, + selectedPayment: null as subscriptionPayment | null, + errorMessages: new ErrorMessages(), + invoiceFormName: '', + invoiceFormAddress: '', + invoiceFormCity: '', + invoiceFormState: '', + invoiceFormZipCode: '', + invoiceFormCountry: '', + invoiceFormNotes: '', + subscriptionInvoiceDialog: false, + countries: [ + { text: 'Afghanistan', value: 'AF' }, + { text: 'Åland Islands', value: 'AX' }, + { text: 'Albania', value: 'AL' }, + { text: 'Algeria', value: 'DZ' }, + { text: 'American Samoa', value: 'AS' }, + { text: 'Andorra', value: 'AD' }, + { text: 'Angola', value: 'AO' }, + { text: 'Anguilla', value: 'AI' }, + { text: 'Antarctica', value: 'AQ' }, + { text: 'Antigua and Barbuda', value: 'AG' }, + { text: 'Argentina', value: 'AR' }, + { text: 'Armenia', value: 'AM' }, + { text: 'Aruba', value: 'AW' }, + { text: 'Australia', value: 'AU' }, + { text: 'Austria', value: 'AT' }, + { text: 'Azerbaijan', value: 'AZ' }, + { text: 'Bahamas', value: 'BS' }, + { text: 'Bahrain', value: 'BH' }, + { text: 'Bangladesh', value: 'BD' }, + { text: 'Barbados', value: 'BB' }, + { text: 'Belarus', value: 'BY' }, + { text: 'Belgium', value: 'BE' }, + { text: 'Belize', value: 'BZ' }, + { text: 'Benin', value: 'BJ' }, + { text: 'Bermuda', value: 'BM' }, + { text: 'Bhutan', value: 'BT' }, + { text: 'Bolivia', value: 'BO' }, + { text: 'Bonaire', value: 'BQ' }, + { text: 'Bosnia and Herzegovina', value: 'BA' }, + { text: 'Botswana', value: 'BW' }, + { text: 'Bouvet Island', value: 'BV' }, + { text: 'Brazil', value: 'BR' }, + { text: 'British Indian Ocean', value: 'IO' }, + { text: 'Brunei Darussalam', value: 'BN' }, + { text: 'Bulgaria', value: 'BG' }, + { text: 'Burkina Faso', value: 'BF' }, + { text: 'Burundi', value: 'BI' }, + { text: 'Cabo Verde', value: 'CV' }, + { text: 'Cambodia', value: 'KH' }, + { text: 'Cameroon', value: 'CM' }, + { text: 'Canada', value: 'CA' }, + { text: 'Cayman Islands', value: 'KY' }, + { text: 'Central African Republic', value: 'CF' }, + { text: 'Chad', value: 'TD' }, + { text: 'Chile', value: 'CL' }, + { text: 'China', value: 'CN' }, + { text: 'Christmas Island', value: 'CX' }, + { text: 'Cocos (Keeling) Islands', value: 'CC' }, + { text: 'Colombia', value: 'CO' }, + { text: 'Comoros', value: 'KM' }, + { text: 'Congo', value: 'CG' }, + { text: 'Congo', value: 'CD' }, + { text: 'Cook Islands', value: 'CK' }, + { text: 'Costa Rica', value: 'CR' }, + { text: "Côte d'Ivoire", value: 'CI' }, + { text: 'Cuba', value: 'CU' }, + { text: 'Curaçao', value: 'CW' }, + { text: 'Cyprus', value: 'CY' }, + { text: 'Czechia', value: 'CZ' }, + { text: 'Denmark', value: 'DK' }, + { text: 'Djibouti', value: 'DJ' }, + { text: 'Dominica', value: 'DM' }, + { text: 'Dominican Republic', value: 'DO' }, + { text: 'Ecuador', value: 'EC' }, + { text: 'Egypt', value: 'EG' }, + { text: 'El Salvador', value: 'SV' }, + { text: 'Equatorial Guinea', value: 'GQ' }, + { text: 'Eritrea', value: 'ER' }, + { text: 'Estonia', value: 'EE' }, + { text: 'Eswatini', value: 'SZ' }, + { text: 'Ethiopia', value: 'ET' }, + { text: 'Falkland Islands', value: 'FK' }, + { text: 'Faroe Islands', value: 'FO' }, + { text: 'Fiji', value: 'FJ' }, + { text: 'Finland', value: 'FI' }, + { text: 'France', value: 'FR' }, + { text: 'French Guiana', value: 'GF' }, + { text: 'French Polynesia', value: 'PF' }, + { text: 'French Southern Territories', value: 'TF' }, + { text: 'Gabon', value: 'GA' }, + { text: 'Gambia', value: 'GM' }, + { text: 'Georgia', value: 'GE' }, + { text: 'Germany', value: 'DE' }, + { text: 'Ghana', value: 'GH' }, + { text: 'Gibraltar', value: 'GI' }, + { text: 'Greece', value: 'GR' }, + { text: 'Greenland', value: 'GL' }, + { text: 'Grenada', value: 'GD' }, + { text: 'Guadeloupe', value: 'GP' }, + { text: 'Guam', value: 'GU' }, + { text: 'Guatemala', value: 'GT' }, + { text: 'Guernsey', value: 'GG' }, + { text: 'Guinea', value: 'GN' }, + { text: 'Guinea-Bissau', value: 'GW' }, + { text: 'Guyana', value: 'GY' }, + { text: 'Haiti', value: 'HT' }, + { text: 'Heard Island and McDonald Islands', value: 'HM' }, + { text: 'Holy See', value: 'VA' }, + { text: 'Honduras', value: 'HN' }, + { text: 'Hong Kong', value: 'HK' }, + { text: 'Hungary', value: 'HU' }, + { text: 'Iceland', value: 'IS' }, + { text: 'India', value: 'IN' }, + { text: 'Indonesia', value: 'ID' }, + { text: 'Iran', value: 'IR' }, + { text: 'Iraq', value: 'IQ' }, + { text: 'Ireland', value: 'IE' }, + { text: 'Isle of Man', value: 'IM' }, + { text: 'Israel', value: 'IL' }, + { text: 'Italy', value: 'IT' }, + { text: 'Jamaica', value: 'JM' }, + { text: 'Japan', value: 'JP' }, + { text: 'Jersey', value: 'JE' }, + { text: 'Jordan', value: 'JO' }, + { text: 'Kazakhstan', value: 'KZ' }, + { text: 'Kenya', value: 'KE' }, + { text: 'Kiribati', value: 'KI' }, + { text: 'North Korea', value: 'KP' }, + { text: 'South Korea', value: 'KR' }, + { text: 'Kuwait', value: 'KW' }, + { text: 'Kyrgyzstan', value: 'KG' }, + { text: 'Lao People’s Democratic Republic', value: 'LA' }, + { text: 'Latvia', value: 'LV' }, + { text: 'Lebanon', value: 'LB' }, + { text: 'Lesotho', value: 'LS' }, + { text: 'Liberia', value: 'LR' }, + { text: 'Libya', value: 'LY' }, + { text: 'Liechtenstein', value: 'LI' }, + { text: 'Lithuania', value: 'LT' }, + { text: 'Luxembourg', value: 'LU' }, + { text: 'Macao', value: 'MO' }, + { text: 'Madagascar', value: 'MG' }, + { text: 'Malawi', value: 'MW' }, + { text: 'Malaysia', value: 'MY' }, + { text: 'Maldives', value: 'MV' }, + { text: 'Mali', value: 'ML' }, + { text: 'Malta', value: 'MT' }, + { text: 'Marshall Islands', value: 'MH' }, + { text: 'Martinique', value: 'MQ' }, + { text: 'Mauritania', value: 'MR' }, + { text: 'Mauritius', value: 'MU' }, + { text: 'Mayotte', value: 'YT' }, + { text: 'Mexico', value: 'MX' }, + { text: 'Micronesia', value: 'FM' }, + { text: 'Moldova', value: 'MD' }, + { text: 'Monaco', value: 'MC' }, + { text: 'Mongolia', value: 'MN' }, + { text: 'Montenegro', value: 'ME' }, + { text: 'Montserrat', value: 'MS' }, + { text: 'Morocco', value: 'MA' }, + { text: 'Mozambique', value: 'MZ' }, + { text: 'Myanmar', value: 'MM' }, + { text: 'Namibia', value: 'NA' }, + { text: 'Nauru', value: 'NR' }, + { text: 'Nepal', value: 'NP' }, + { text: 'Netherlands', value: 'NL' }, + { text: 'New Caledonia', value: 'NC' }, + { text: 'New Zealand', value: 'NZ' }, + { text: 'Nicaragua', value: 'NI' }, + { text: 'Niger', value: 'NE' }, + { text: 'Nigeria', value: 'NG' }, + { text: 'Niue', value: 'NU' }, + { text: 'Norfolk Island', value: 'NF' }, + { text: 'North Macedonia', value: 'MK' }, + { text: 'Northern Mariana Islands', value: 'MP' }, + { text: 'Norway', value: 'NO' }, + { text: 'Oman', value: 'OM' }, + { text: 'Pakistan', value: 'PK' }, + { text: 'Palau', value: 'PW' }, + { text: 'Panama', value: 'PA' }, + { text: 'Papua New Guinea', value: 'PG' }, + { text: 'Paraguay', value: 'PY' }, + { text: 'Peru', value: 'PE' }, + { text: 'Philippines', value: 'PH' }, + { text: 'Pitcairn', value: 'PN' }, + { text: 'Poland', value: 'PL' }, + { text: 'Portugal', value: 'PT' }, + { text: 'Puerto Rico', value: 'PR' }, + { text: 'Qatar', value: 'QA' }, + { text: 'Réunion', value: 'RE' }, + { text: 'Romania', value: 'RO' }, + { text: 'Russian Federation', value: 'RU' }, + { text: 'Rwanda', value: 'RW' }, + { text: 'Saint Barthélemy', value: 'BL' }, + { text: 'Saint Helena, Ascension and Tristan da Cunha', value: 'SH' }, + { text: 'Saint Kitts and Nevis', value: 'KN' }, + { text: 'Saint Lucia', value: 'LC' }, + { text: 'Saint Martin (French part)', value: 'MF' }, + { text: 'Saint Pierre and Miquelon', value: 'PM' }, + { text: 'Saint Vincent and the Grenadines', value: 'VC' }, + { text: 'Samoa', value: 'WS' }, + { text: 'San Marino', value: 'SM' }, + { text: 'Sao Tome and Principe', value: 'ST' }, + { text: 'Saudi Arabia', value: 'SA' }, + { text: 'Senegal', value: 'SN' }, + { text: 'Serbia', value: 'RS' }, + { text: 'Seychelles', value: 'SC' }, + { text: 'Sierra Leone', value: 'SL' }, + { text: 'Singapore', value: 'SG' }, + { text: 'Slovakia', value: 'SK' }, + { text: 'Slovenia', value: 'SI' }, + { text: 'Solomon Islands', value: 'SB' }, + { text: 'Somalia', value: 'SO' }, + { text: 'South Africa', value: 'ZA' }, + { text: 'South Georgia and the South Sandwich Islands', value: 'GS' }, + { text: 'South Sudan', value: 'SS' }, + { text: 'Spain', value: 'ES' }, + { text: 'Sri Lanka', value: 'LK' }, + { text: 'Sudan', value: 'SD' }, + { text: 'Suriname', value: 'SR' }, + { text: 'Svalbard and Jan Mayen', value: 'SJ' }, + { text: 'Sweden', value: 'SE' }, + { text: 'Switzerland', value: 'CH' }, + { text: 'Syrian Arab Republic', value: 'SY' }, + { text: 'Taiwan, Province of China', value: 'TW' }, + { text: 'Tajikistan', value: 'TJ' }, + { text: 'Tanzania, United Republic of', value: 'TZ' }, + { text: 'Thailand', value: 'TH' }, + { text: 'Timor-Leste', value: 'TL' }, + { text: 'Togo', value: 'TG' }, + { text: 'Tokelau', value: 'TK' }, + { text: 'Tonga', value: 'TO' }, + { text: 'Trinidad and Tobago', value: 'TT' }, + { text: 'Tunisia', value: 'TN' }, + { text: 'Turkey', value: 'TR' }, + { text: 'Turkmenistan', value: 'TM' }, + { text: 'Turks and Caicos Islands', value: 'TC' }, + { text: 'Tuvalu', value: 'TV' }, + { text: 'Uganda', value: 'UG' }, + { text: 'Ukraine', value: 'UA' }, + { text: 'United Arab Emirates', value: 'AE' }, + { text: 'United Kingdom', value: 'GB' }, + { text: 'United States', value: 'US' }, + { text: 'United States Minor Outlying Islands', value: 'UM' }, + { text: 'Uruguay', value: 'UY' }, + { text: 'Uzbekistan', value: 'UZ' }, + { text: 'Vanuatu', value: 'VU' }, + { text: 'Venezuela', value: 'VE' }, + { text: 'Viet Nam', value: 'VN' }, + { text: 'Virgin Islands (British)', value: 'VG' }, + { text: 'Virgin Islands (U.S.)', value: 'VI' }, + { text: 'Wallis and Futuna', value: 'WF' }, + { text: 'Western Sahara', value: 'EH' }, + { text: 'Yemen', value: 'YE' }, + { text: 'Zambia', value: 'ZM' }, + { text: 'Zimbabwe', value: 'ZW' }, + ], plans: [ { name: 'Free', @@ -424,6 +931,12 @@ export default Vue.extend({ messagesPerMonth: 20000, price: 350, }, + { + name: '50k - Monthly', + id: '50k-monthly', + messagesPerMonth: 50000, + price: 89, + }, { name: '100k - Monthly', id: '100k-monthly', @@ -431,10 +944,10 @@ export default Vue.extend({ price: 175, }, { - name: '100k - Yearly', - id: '100k-yearly', - messagesPerMonth: 100000, - price: 1750, + name: '200k - Monthly', + id: '200k-monthly', + messagesPerMonth: 200000, + price: 350, }, { name: 'PRO - Lifetime', @@ -451,6 +964,81 @@ export default Vue.extend({ } }, computed: { + invoiceStateOptions() { + if (this.invoiceFormCountry === 'US') { + return [ + { text: 'Alabama', value: 'AL' }, + { text: 'Alaska', value: 'AK' }, + { text: 'Arizona', value: 'AZ' }, + { text: 'Arkansas', value: 'AR' }, + { text: 'California', value: 'CA' }, + { text: 'Colorado', value: 'CO' }, + { text: 'Connecticut', value: 'CT' }, + { text: 'Delaware', value: 'DE' }, + { text: 'Florida', value: 'FL' }, + { text: 'Georgia', value: 'GA' }, + { text: 'Hawaii', value: 'HI' }, + { text: 'Idaho', value: 'ID' }, + { text: 'Illinois', value: 'IL' }, + { text: 'Indiana', value: 'IN' }, + { text: 'Iowa', value: 'IA' }, + { text: 'Kansas', value: 'KS' }, + { text: 'Kentucky', value: 'KY' }, + { text: 'Louisiana', value: 'LA' }, + { text: 'Maine', value: 'ME' }, + { text: 'Maryland', value: 'MD' }, + { text: 'Massachusetts', value: 'MA' }, + { text: 'Michigan', value: 'MI' }, + { text: 'Minnesota', value: 'MN' }, + { text: 'Mississippi', value: 'MS' }, + { text: 'Missouri', value: 'MO' }, + { text: 'Montana', value: 'MT' }, + { text: 'Nebraska', value: 'NE' }, + { text: 'Nevada', value: 'NV' }, + { text: 'New Hampshire', value: 'NH' }, + { text: 'New Jersey', value: 'NJ' }, + { text: 'New Mexico', value: 'NM' }, + { text: 'New York', value: 'NY' }, + { text: 'North Carolina', value: 'NC' }, + { text: 'North Dakota', value: 'ND' }, + { text: 'Ohio', value: 'OH' }, + { text: 'Oklahoma', value: 'OK' }, + { text: 'Oregon', value: 'OR' }, + { text: 'Pennsylvania', value: 'PA' }, + { text: 'Rhode Island', value: 'RI' }, + { text: 'South Carolina', value: 'SC' }, + { text: 'South Dakota', value: 'SD' }, + { text: 'Tennessee', value: 'TN' }, + { text: 'Texas', value: 'TX' }, + { text: 'Utah', value: 'UT' }, + { text: 'Vermont', value: 'VT' }, + { text: 'Virginia', value: 'VA' }, + { text: 'Washington', value: 'WA' }, + { text: 'West Virginia', value: 'WV' }, + { text: 'Wisconsin', value: 'WI' }, + { text: 'Wyoming', value: 'WY' }, + { text: 'District of Columbia', value: 'DC' }, + ] + } + if (this.invoiceFormCountry === 'CA') { + return [ + { text: 'Alberta', value: 'AB' }, + { text: 'British Columbia', value: 'BC' }, + { text: 'Manitoba', value: 'MB' }, + { text: 'New Brunswick', value: 'NB' }, + { text: 'Newfoundland and Labrador', value: 'NL' }, + { text: 'Nova Scotia', value: 'NS' }, + { text: 'Ontario', value: 'ON' }, + { text: 'Prince Edward Island', value: 'PE' }, + { text: 'Quebec', value: 'QC' }, + { text: 'Saskatchewan', value: 'SK' }, + { text: 'Northwest Territories', value: 'NT' }, + { text: 'Nunavut', value: 'NU' }, + { text: 'Yukon', value: 'YT' }, + ] + } + return [] + }, checkoutURL() { const url = new URL(this.$config.checkoutURL) const user = this.$store.getters.getAuthUser @@ -505,7 +1093,51 @@ export default Vue.extend({ this.$store.dispatch('loadBillingUsageHistory'), ]) this.loading = false + this.loadSubscriptionInvoices() }, + + loadSubscriptionInvoices() { + this.loadingSubscriptionPayments = true + this.$store + .dispatch('indexSubscriptionPayments') + .then((response: ResponsesUserSubscriptionPaymentsResponse) => { + this.payments = response + }) + .finally(() => { + this.loadingSubscriptionPayments = false + }) + }, + + generateInvoice() { + this.errorMessages = new ErrorMessages() + this.loading = true + this.$store + .dispatch('generateSubscriptionPaymentInvoice', { + subscriptionInvoiceId: this.selectedPayment?.id || '', + request: { + name: this.invoiceFormName, + address: this.invoiceFormAddress, + city: this.invoiceFormCity, + state: this.invoiceFormState, + zip_code: this.invoiceFormZipCode, + country: this.invoiceFormCountry, + notes: this.invoiceFormNotes, + }, + } as { + subscriptionInvoiceId: string + request: RequestsUserPaymentInvoice + }) + .then(() => { + this.subscriptionInvoiceDialog = false + }) + .catch((error: ErrorMessages) => { + this.errorMessages = error + }) + .finally(() => { + this.loading = false + }) + }, + updateDetails() { this.loading = true this.$store @@ -532,6 +1164,11 @@ export default Vue.extend({ this.loading = false }) }, + + showInvoiceDialog(payment: subscriptionPayment) { + this.selectedPayment = payment + this.subscriptionInvoiceDialog = true + }, }, }) diff --git a/web/pages/blog/end-to-end-encryption-to-sms-messages.vue b/web/pages/blog/end-to-end-encryption-to-sms-messages.vue new file mode 100644 index 00000000..729acdd9 --- /dev/null +++ b/web/pages/blog/end-to-end-encryption-to-sms-messages.vue @@ -0,0 +1,341 @@ + + + diff --git a/web/pages/blog/grant-send-and-read-sms-permissions-on-android.vue b/web/pages/blog/grant-send-and-read-sms-permissions-on-android.vue new file mode 100644 index 00000000..72540fa0 --- /dev/null +++ b/web/pages/blog/grant-send-and-read-sms-permissions-on-android.vue @@ -0,0 +1,144 @@ + + + diff --git a/web/pages/blog/index.vue b/web/pages/blog/index.vue index d4236d3f..992837b0 100644 --- a/web/pages/blog/index.vue +++ b/web/pages/blog/index.vue @@ -3,7 +3,15 @@ - + +

    Blog

    +

    + Learn more about httpSMS through our blog! +

    + + + + - Convert your android phone into an SMS gateway. + Convert your Android phone into an SMS gateway.

    - Use your android phone to send and receive SMS messages using a - simple HTTP API. + Save money by using your + phone to send and receive SMS messages via a simple programmable API + with end-to-end encryption.

    - ⚡Trusted by 2,247+ happy users who have sent or received - more than 417,220+ messages. + ⚡Trusted by 16,212+ happy users who have sent or received + more than 5,921,545+ messages.

    100% Open Source
    +
    + + Uneed POTD1 Badge + +
    excel template - and upload it on httpSMS to send your SMS messages to multiple + and upload it on httpSMS to send SMS messages to up to 1,000 recipients at once without writing any code. + + {{ mdiMicrosoftExcel }} + Integration Guide +
    @@ -129,7 +146,7 @@ >
    - +

    @@ -148,9 +165,10 @@

    Zapier Integration Guide + Zapier Integration Guide +
    @@ -162,7 +180,7 @@ >
    - +

    Webhooks

    @@ -172,6 +190,14 @@ your Android phone to your server using a callback URL which you provide. + + {{ mdiWebhook }} + Documentation +
    @@ -183,15 +209,23 @@ >
    - +

    Control Sending

    - Send bulk messages without mobile carrier limitations. If you - set a rate e.g 3 messages per minute, we will queue up your - messages and send them at a rate of 1 message per 20 seconds. + Send SMS messages without going over your mobile carrier + limitations. If you set a rate e.g 3 messages per minute, we + will queue up your messages and send them at a rate of 1 message + per 20 seconds.
    + + {{ mdiArrowRightThin }}Documentation +
    @@ -203,7 +237,7 @@ >
    - +

    Monitoring

    @@ -222,7 +256,7 @@ > - +

    Open Source

    @@ -251,6 +285,90 @@ > + + +
    +

    Encryption 🔐

    +
    + Take control of your privacy with our end-to-end encrypted SMS + feature. Safeguard your messages from prying eyes, ensuring + absolute confidentiality using the military grade + AES-256 encryption + algorithm. +
    + Setup end-to-end encryption +
    +
    + + + +
    + + +
    +

    Multiple Phones

    +
    + Setup the httpSMS gateway Android app on multiple phones + independently and securely without sharing data under one + account by creating unique phone API keys. +
    + + {{ mdiCellphoneKey }}Documentation + +
    +
    + + + +
    + + +
    +

    Schedule Text Messages

    +
    + Control when your SMS will reach your recipients, allowing you + to perfectly time promotions, critical alerts etc by scheduling + your messages in advance. +
    + {{ mdiClockOutline }}Documentation +
    +
    + + + +
    @@ -370,23 +488,18 @@
    -let apiKey = "Get API Key from https://httpsms.com/settings";
    +import HttpSms from 'httpsms'
     
    -fetch('https://api.httpsms.com/v1/messages/send', {
    -    method: 'POST',
    -    headers: {
    -        'x-api-key': apiKey,
    -        'Accept': 'application/json',
    -        'Content-Type': 'application/json'
    -    },
    -    body: JSON.stringify({
    -        "content": "This is a sample text message",
    -        "from": "+18005550199",
    -        "to": "+18005550100"
    -    })
    +const client = new HttpSms('' /* Get the API Key from https://httpsms.com/settings */);
    +
    +client.messages.postSend({
    +    content:   'This is a sample text message',
    +    from:      '+18005550199', // Put the correct phone number here
    +    to:        '+18005550100', // Put the correct phone number here
    +})
    +.then((message) => {
    +    console.log(message.id); // log the ID of the sent message
     })
    -.then(res => res.json())
    -.then((data) => console.log(data));
     
                         
    @@ -555,6 +668,26 @@ Console.WriteLine(await response.Content.ReadAsStringAsync());
    + + + + + + + @@ -586,7 +719,7 @@ Console.WriteLine(await response.Content.ReadAsStringAsync()); {{ mdiCheckCircle }}Forward received messages to your server + >Forward received messages via webhook

    {{ @@ -635,35 +768,40 @@ Console.WriteLine(await response.Content.ReadAsStringAsync()); {{ mdiCheckCircle }}Forward received messages to your server + >Forward received messages via webhook

    {{ mdiCheckCircle }}Priority email support + >Priority support

    - + -

    Ultra

    +

    + {{ pricingLabels[pricing] }} Plan +

    - Send and receive up to 10,000 SMS messages like a power user. + Send and receive up to {{ planMessages }} SMS messages like a + power user.

    - $20/month + ${{ planMonthlyPrice }}/month

    - $200/year + ${{ planYearlyPrice }}/year

    - or $200 per year + or ${{ planYearlyPrice }} per year

    - or $16.66 per month + or ${{ planYearlyMonthlyPrice }} per month

    Try For Free{{ mdiCheckCircle }}Send or receive up to 10,000 SMS/month + >Send or receive up to + {{ pricingLabels[pricing] }} SMS/month

    @@ -683,39 +822,203 @@ Console.WriteLine(await response.Content.ReadAsStringAsync()); {{ mdiCheckCircle }}Forward received messages to your server + >Forward received messages via webhook

    {{ mdiCheckCircle }}Priority email support + >Priority support

    - - - -

    - Subscribe to my newsletter where I share new features and - updates on httpSMS. -

    - -
    -
    -
    -
    + + + + Feel free to contact us if + you need a bigger plan, or if you want us to install the httpSMS + API on your dedicated server. If would still like to support us, + please donate via + GitHub Sponsors + ❤️ + + + + + + + + +
    + + + +
    +

    Joysankar M.

    + +
    + +
    + +
    +
    +

    + "httpSMS is free platform which transforms your phone into an + sms server! It has no hard limit also. It is an + innovative idea, I have not seen such tech before. If you + have an sms active pack in your phone then good to go + with httpSMS." +

    +
    +
    +
    + + + +
    + + + +
    +

    Edmund Ciego

    + +
    + +
    + +
    +
    +

    + "Outstanding product. Literally have been using this for + years since we don't have an sms gateways that can handle http + requests costing less than 50 cent per sms in my Country. + Love the product and the support! Great work Arnold!" +

    +
    +
    +
    +
    + + +

    Frequently Asked Questions

    +

    + If you still cannot find the answer to your question, + send us an email or ask in + our Discord channel. +

    +
    +
    + + + + + + Can I install the app on my Iphone? + + + +

    + The httpSMS application works only on Android phones at the + moment since Apple doesn't allow you to install a custom SMS + messaging app. +

    +
    +
    + + + What's the minimum supported Android version? + + + +

    + The httpSMS Android app works from Android 9 (Pie) and above. + So you can install the application on your old Android phone + which you don't use anymore. +

    +
    +
    + + + Can I send unlimited number of messages per month? + + + +

    + We do have packages that allow up to 100,000 SMS messages per + month but you can can + send us an email if + you will like to send more messages so we create a custom plan + just for you. +

    +
    +
    + + + Can I change the sender of the SMS message + + + +

    + No you cannot. When you send an SMS message using the httpSMS + app it uses your SIM card to send the message so the recipient + will see your phone number as the sender of the SMS. You + cannot use your brand name as the sender ID. +

    +
    +
    +
    +
    +
    +
    @@ -726,20 +1029,29 @@ import { mdiCheckCircle, mdiSend, mdiGift, + mdiLightbulbOn60, mdiForum, mdiCreation, mdiNumeric1, + mdiSale, mdiLanguagePython, mdiNumeric2, mdiNumeric3, + mdiCellphoneKey, mdiTallyMark1, mdiTallyMark3, mdiTallyMark2, mdiLabel, mdiLanguageJavascript, mdiLanguagePhp, + mdiPlus, + mdiMinus, mdiLanguageCsharp, mdiLanguageJava, + mdiMicrosoftExcel, + mdiWebhook, + mdiClockOutline, + mdiArrowRightThin, mdiPowershell, mdiLanguageGo, } from '@mdi/js' @@ -749,39 +1061,60 @@ export default Vue.extend({ layout: 'website', data() { return { + mdiMicrosoftExcel, + mdiWebhook, mdiGithub, mdiLabel, + mdiLightbulbOn60, mdiCheckCircle, mdiSend, mdiGift, + mdiArrowRightThin, + mdiClockOutline, mdiCreation, mdiForum, mdiNumeric1, mdiNumeric2, mdiNumeric3, mdiTallyMark1, + mdiSale, mdiTallyMark2, + mdiPlus, + mdiMinus, mdiTallyMark3, mdiLanguageJavascript, mdiLanguagePhp, mdiLanguagePython, mdiLanguageCsharp, mdiLanguageJava, + mdiCellphoneKey, mdiPowershell, mdiLanguageGo, selectedTab: 'javascript', yearlyPricing: false, - substackLoaded: false, + faqPanel: null, + pricing: 0, + pricingLabels: ['10K', '20K', '50K', '100K', '200K'], + pricingLabelsFull: ['10,000', '20,000', '50,000', '100,000', '200,000'], } }, - mounted() { - setTimeout(() => { - const s = document.createElement('script') - s.type = 'text/javascript' - s.src = 'https://substackapi.com/widget.js' - document.getElementsByTagName('head')[0].appendChild(s) - this.substackLoaded = true - }, 5000) + computed: { + planMessages() { + const plan = this.pricingLabels[this.pricing] + return plan.replace('K', ',000') + }, + planMonthlyPrice() { + const prices = [20, 35, 89, 175, 350] + return prices[this.pricing] + }, + planYearlyPrice() { + const prices = [200, 350, 1068, 2100, 4200] + return prices[this.pricing] + }, + planYearlyMonthlyPrice() { + const prices = [16.66, 29.16, 89, 175, 350] + return prices[this.pricing] + }, }, }) @@ -806,4 +1139,8 @@ export default Vue.extend({ -webkit-background-clip: text; -webkit-text-fill-color: transparent; } + +.gradient-underline { + color: white; +} diff --git a/web/pages/login.vue b/web/pages/login.vue index afce3a72..1339b471 100644 --- a/web/pages/login.vue +++ b/web/pages/login.vue @@ -14,9 +14,9 @@ Welcome

    - Join 2,247+ happy users who have sent or + Join 16,212+ happy users who have sent or
    - received more than 417,220+ SMS messages + received more than 5,921,545+ SMS messages

    diff --git a/web/pages/messages/index.vue b/web/pages/messages/index.vue index a1a2a154..62fccf85 100644 --- a/web/pages/messages/index.vue +++ b/web/pages/messages/index.vue @@ -16,6 +16,7 @@ + x.trim() !== '') + .map((x) => x.trim()), sim: this.simSelected.code, }) .then(() => { @@ -113,6 +136,9 @@ export default { ), ) } + if (response.data.data.attachments) { + errors.set('attachments', response.data.data.attachments) + } if (response.data.data.from) { this.$store.dispatch('addNotification', { message: response.data.data.from[0], diff --git a/web/pages/phone-api-keys/index.vue b/web/pages/phone-api-keys/index.vue new file mode 100644 index 00000000..e83a2d70 --- /dev/null +++ b/web/pages/phone-api-keys/index.vue @@ -0,0 +1,483 @@ + + + + diff --git a/web/pages/privacy-policy/index.vue b/web/pages/privacy-policy/index.vue index 53e786dd..0e618d64 100644 --- a/web/pages/privacy-policy/index.vue +++ b/web/pages/privacy-policy/index.vue @@ -4,17 +4,17 @@

    Privacy Policy

    - Ndole Studio built the HTTP SMS service as an open source project. - This SERVICE is provided by Ndole Studio and is intended for use as - is. This page is used to inform visitors regarding our policies with - the collection, use, and disclosure of Personal Information if anyone + Ndole Studio built the httpSMS service as an open source project. This + SERVICE is provided by Ndole Studio and is intended for use as is. + This page is used to inform visitors regarding our policies with the + collection, use, and disclosure of Personal Information if anyone decided to use our Service. If you choose to use our Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that we collect is used for providing and improving the Service. We will not use or share your information with anyone except as described in this Privacy Policy. The terms used in this Privacy Policy have the same meanings as in our Terms and - Conditions, which are accessible at HTTP SMS unless otherwise defined + Conditions, which are accessible at httpSMS unless otherwise defined in this Privacy Policy.

    Information Collection and Use

    @@ -26,9 +26,9 @@ policy.

    - HTTP SMS does use third-party services that may collect information + httpSMS does use third-party services that may collect information used to identify you. Link to the privacy policy of third-party - service providers used by HTTP SMS + service providers used by httpSMS

    Log Data

    diff --git a/web/pages/search-messages/index.vue b/web/pages/search-messages/index.vue new file mode 100644 index 00000000..9fd1f667 --- /dev/null +++ b/web/pages/search-messages/index.vue @@ -0,0 +1,531 @@ + + + diff --git a/web/pages/settings/index.vue b/web/pages/settings/index.vue index 060650d2..6d3415d0 100644 --- a/web/pages/settings/index.vue +++ b/web/pages/settings/index.vue @@ -47,7 +47,7 @@ style="max-width: 250px" label="Timezone" :items="timezones" - @change="updateUser" + @change="updateTimezone" >

    API Key
    @@ -75,21 +75,101 @@ class="mb-n2" @click:append="apiKeyShow = !apiKeyShow" > -
    +
    + + {{ mdiQrcode }} + Show QR Code + + + + API Key QR Code + Scan this QR code with the + httpSMS app + on your Android phone to login. + + + + + Close + + + Documentation + + + + + + Are you sure you want to rotate your API Key? + + + You will have to logout and login again on the + httpSMS Android app with your new API key after you + rotate it. + + + + {{ mdiRefresh }} + Yes Rotate Key + + + + Close + + + +
    Webhooks

    @@ -113,7 +193,7 @@ ID - Callback URL + Callback URL Events @@ -125,7 +205,7 @@ {{ webhook.id }} - {{ webhook.url }} + {{ webhook.url }} Phone Number - - Fcm Token - Retries @@ -260,16 +337,6 @@ {{ phone.id }} {{ phone.phone_number | phoneNumber }} - -

    - -
    -
    {{ @@ -332,20 +399,93 @@ hint="This switch controls email notifications we send when we your message is failed or expired." persistent-hint > + - + {{ mdiContentSave }} Save Notification Settings +
    + Delete Account +
    +

    + You cannot delete your account because you have an active + subscription on httpSMS. + Cancel your subscription + before deleting your account. +

    +

    + You can delete all your data on httpSMS by clicking the button + below. This action is irreversible and all your data will + be permanently deleted from the httpSMS database instantly and it + cannot be recovered. +

    + + {{ mdiDelete }} + Delete your Account + + + + Delete your httpSMS account + + Are you sure you want to delete your account? This action is + irreversible and all your data will be permanently + deleted from the httpSMS database instantly. + + + + {{ + mdiDelete + }} + Delete My Account + + + + Keep My account + Close + + + +
    - + Edit Phone @@ -409,6 +549,17 @@ label="Max Send Attempts" > + + @@ -430,7 +581,7 @@ - + Add a new  @@ -545,7 +696,7 @@ - + Add a new  @@ -671,10 +822,13 @@ import { mdiContentSave, mdiConnection, mdiEye, + mdiRefresh, mdiLinkVariant, mdiEyeOff, mdiSquareEditOutline, + mdiQrcode, } from '@mdi/js' +import { toCanvas } from 'qrcode' import { ErrorMessages } from '~/plugins/errors' import LoadingButton from '~/components/LoadingButton.vue' @@ -686,10 +840,12 @@ export default Vue.extend({ return { mdiEye, mdiEyeOff, + mdiRefresh, mdiArrowLeft, mdiAccountCircle, mdiShieldCheck, mdiDelete, + mdiQrcode, mdiLinkVariant, mdiContentSave, mdiSquareEditOutline, @@ -698,6 +854,11 @@ export default Vue.extend({ apiKeyShow: false, showPhoneEdit: false, showDiscordEdit: false, + showRotateApiKey: false, + rotatingApiKey: false, + showQrCodeDialog: false, + deletingAccount: false, + showDeleteAccountDialog: false, activeWebhook: { id: null, url: '', @@ -709,12 +870,14 @@ export default Vue.extend({ id: null, name: '', server_id: '', + missed_call_auto_reply: '', incoming_channel_id: '', }, updatingEmailNotifications: false, notificationSettings: { webhook_enabled: true, message_status_enabled: true, + newsletter_enabled: true, heartbeat_enabled: true, }, updatingWebhook: false, @@ -732,6 +895,9 @@ export default Vue.extend({ 'message.phone.delivered', 'message.send.failed', 'message.send.expired', + 'message.call.missed', + 'phone.heartbeat.offline', + 'phone.heartbeat.online', ], } }, @@ -747,6 +913,12 @@ export default Vue.extend({ } return this.$store.getters.getUser.api_key }, + hasActiveSubscription() { + if (this.$store.getters.getUser === null) { + return true + } + return this.$store.getters.getUser.subscription_renews_at != null + }, timezones() { return Intl.supportedValuesOf('timeZone') }, @@ -756,6 +928,15 @@ export default Vue.extend({ }) }, }, + watch: { + showQrCodeDialog(newVal) { + if (newVal && this.apiKey) { + this.$nextTick(() => { + this.generateQrCode(this.apiKey) + }) + } + }, + }, async mounted() { await Promise.all([ this.$store.dispatch('clearAxiosError'), @@ -771,6 +952,19 @@ export default Vue.extend({ }, methods: { + generateQrCode(text) { + const canvas = this.$refs.qrCodeCanvas + if (canvas) { + toCanvas(canvas, text, { errorCorrectionLevel: 'H' }, (err) => { + if (err) { + this.$store.dispatch('addNotification', { + message: 'Failed to generate API key QR code', + type: 'error', + }) + } + }) + } + }, updateEmailNotifications() { this.notificationSettings = { webhook_enabled: @@ -779,6 +973,8 @@ export default Vue.extend({ this.$store.getters.getUser.notification_message_status_enabled, heartbeat_enabled: this.$store.getters.getUser.notification_heartbeat_enabled, + newsletter_enabled: + this.$store.getters.getUser.notification_newsletter_enabled, } }, showEditPhone(phoneId) { @@ -850,6 +1046,7 @@ export default Vue.extend({ name: '', server_id: '', incoming_channel_id: '', + missed_call_auto_reply: '', } this.showDiscordEdit = true this.resetErrors() @@ -967,13 +1164,10 @@ export default Vue.extend({ }) }, - updateUser(timezone) { + updateTimezone(timezone) { this.resetErrors() this.$store - .dispatch('updateUser', { - owner: this.$store.getters.getOwner, - timezone, - }) + .dispatch('updateTimezone', timezone) .then(() => { this.$store.dispatch('addNotification', { message: 'Timezone updated successfully', @@ -1009,6 +1203,16 @@ export default Vue.extend({ }) }, + rotateApiKey() { + this.rotatingApiKey = true + this.$store + .dispatch('rotateApiKey', this.$store.getters.getUser.id) + .finally(() => { + this.rotatingApiKey = false + this.showRotateApiKey = false + }) + }, + deleteWebhook(webhookId) { this.updatingWebhook = true this.$store @@ -1050,6 +1254,31 @@ export default Vue.extend({ }) }, + deleteUserAccount() { + this.deletingAccount = true + this.$store + .dispatch('deleteUserAccount') + .then((message) => { + this.$store.dispatch('addNotification', { + message: message ?? 'Your account has been deleted successfully', + type: 'success', + }) + this.$fire.auth.signOut().then(() => { + this.$store.dispatch('setAuthUser', null) + this.$store.dispatch('resetState') + this.$store.dispatch('addNotification', { + type: 'info', + message: 'You have successfully logged out', + }) + this.$router.push({ name: 'index' }) + }) + }) + .finally(() => { + this.deletingAccount = false + this.showDeleteAccountDialog = false + }) + }, + deletePhone(phoneId) { this.updatingPhone = true this.$store.dispatch('deletePhone', phoneId).finally(() => { diff --git a/web/pages/threads/_id/index.vue b/web/pages/threads/_id/index.vue index dfa35441..fc0e27f3 100644 --- a/web/pages/threads/_id/index.vue +++ b/web/pages/threads/_id/index.vue @@ -59,6 +59,19 @@ + + + {{ mdiDelete }} + + + + Delete Thread + + + @@ -90,12 +103,15 @@ > {{ mdiAccount }} - + + {{ mdiCallMissed }} + + - + {{ mdiRefresh }} @@ -113,6 +132,26 @@ + + + {{ mdiContentCopy }} + + + + Copy Message ID + + + + + + {{ mdiDelete }} + + + + Delete Message + + + @@ -123,10 +162,34 @@ :color="isMT(message) ? 'primary' : 'default'" > {{ message.content }} + {{ + message.content + }} + Missed phone call + + + + + + {{ + mdiPaperclip + }} + {{ formatAttachmentName(attachment) }} + +

    @@ -182,6 +245,50 @@

    + + + + + + + {{ mdiRefresh }} + + + + Resend Message + + + + + + {{ mdiContentCopy }} + + + + Copy Message ID + + + + + + {{ mdiDelete }} + + + + Delete Message + + + + + +
    @@ -243,6 +350,9 @@ import { mdiDotsVertical, mdiArrowLeft, mdiCheckAll, + mdiDelete, + mdiCallMissed, + mdiPaperclip, mdiCheck, mdiAlert, mdiPackageUp, @@ -250,9 +360,11 @@ import { mdiAccount, mdiRefresh, mdiSim, + mdiContentCopy, } from '@mdi/js' +import Pusher, { Channel } from 'pusher-js' import { Message } from '~/models/message' -import { SendMessageRequest, SIM } from '~/store' +import { NotificationRequest, SendMessageRequest, SIM } from '~/store' export default Vue.extend({ middleware: ['auth'], @@ -268,14 +380,18 @@ export default Vue.extend({ mdiDotsVertical, mdiArrowLeft, mdiCheckAll, + mdiCallMissed, + mdiPaperclip, mdiCheck, mdiAlert, + mdiDelete, hideMessages: true, loadingMessages: false, messages: [] as Message[], mdiPackageUp, mdiPackageDown, mdiAccount, + mdiContentCopy, mdiRefresh, mdiSim, simOptions: [ @@ -287,6 +403,7 @@ export default Vue.extend({ formMessage: '', formMessageRules, submitting: false, + webhookChannel: null as Channel | null, selectedMenuItem: -1, } }, @@ -315,9 +432,43 @@ export default Vue.extend({ async mounted() { await this.loadData() + + const pusher = new Pusher(this.$config.pusherKey, { + cluster: this.$config.pusherCluster, + }) + this.webhookChannel = pusher.subscribe(this.$store.getters.getAuthUser.id) + this.webhookChannel.bind('message.phone.sent', () => { + if (!this.loadingMessages) { + this.loadMessages(false) + } + }) + this.webhookChannel.bind('message.send.failed', () => { + if (!this.loadingMessages) { + this.loadMessages(false) + } + }) + this.webhookChannel.bind('message.phone.received', () => { + if (!this.loadingMessages) { + this.loadMessages(false) + } + }) + }, + + beforeDestroy() { + if (this.webhookChannel) { + this.webhookChannel.unsubscribe() + } }, methods: { + formatAttachmentName(url: string): string { + const parts = url.split('/') + if (parts.length >= 2) { + return '/' + parts.slice(-2).join('/') + } + return url + }, + isPending(message: Message): boolean { return ['sending', 'pending', 'scheduled'].includes(message.status) }, @@ -351,12 +502,12 @@ export default Vue.extend({ .finally(() => { setTimeout(() => { this.loadingMessages = false - }, 900) + }, 1100) }) this.hideMessages = hideMessages setTimeout(() => { this.scrollToElement() - }, 750) + }, 950) }, async loadData() { @@ -374,6 +525,14 @@ export default Vue.extend({ return message.type === 'mobile-terminated' }, + isMo(message: Message): boolean { + return message.type === 'mobile-originated' + }, + + isMissedCall(message: Message): boolean { + return message.type === 'call/missed' + }, + scrollToElement() { const el: Element = this.$refs.messageBody as Element if (el) { @@ -416,6 +575,34 @@ export default Vue.extend({ this.loadMessages(false) }, + async deleteMessage(message: Message) { + await this.$store.dispatch('deleteMessage', message.id) + + setTimeout(() => { + this.selectedMenuItem = -1 + }, 1000) + + this.loadMessages(false) + }, + + async copyMessageId(message: Message) { + await navigator.clipboard.writeText(message.id).then(() => { + this.$store.dispatch('addNotification', { + message: 'Message ID copied to clipboard', + type: 'success', + } as NotificationRequest) + }) + + setTimeout(() => { + this.selectedMenuItem = -1 + }, 1000) + }, + + async deleteThread(threadID: string) { + await this.$store.dispatch('deleteThread', threadID) + await this.$router.push({ name: 'threads' }) + }, + async sendMessage(event: KeyboardEvent) { if (event.shiftKey) { return @@ -469,6 +656,10 @@ export default Vue.extend({ } } +.hover\:text-decoration-underline:hover { + text-decoration: underline !important; +} + .no-scrollbar, .no-scrollbar textarea { overflow-x: hidden; diff --git a/web/pages/threads/index.vue b/web/pages/threads/index.vue index 97dafb61..d057b1d6 100644 --- a/web/pages/threads/index.vue +++ b/web/pages/threads/index.vue @@ -26,8 +26,8 @@
    - - + +
    diff --git a/web/plugins/axios.ts b/web/plugins/axios.ts index 83b1f27c..5bed0603 100644 --- a/web/plugins/axios.ts +++ b/web/plugins/axios.ts @@ -1,7 +1,7 @@ import axios from 'axios' const client = axios.create({ - baseURL: process.env.BASE_URL || 'http://localhost:8000', + baseURL: process.env.API_BASE_URL || 'http://localhost:8000', headers: { 'X-Client-Version': process.env.GITHUB_SHA || 'dev', }, diff --git a/web/plugins/filters.ts b/web/plugins/filters.ts index 5c838046..2a7c1a99 100644 --- a/web/plugins/filters.ts +++ b/web/plugins/filters.ts @@ -2,7 +2,7 @@ import Vue from 'vue' import { intervalToDuration, formatDuration } from 'date-fns' import { parsePhoneNumber, isValidPhoneNumber } from 'libphonenumber-js' -Vue.filter('phoneNumber', (value: string): string => { +export const formatPhoneNumber = (value: string) => { if (!isValidPhoneNumber(value)) { return value } @@ -11,6 +11,10 @@ Vue.filter('phoneNumber', (value: string): string => { return phoneNumber.formatInternational() } return value +} + +Vue.filter('phoneNumber', (value: string): string => { + return formatPhoneNumber(value) }) Vue.filter('phoneCountry', (value: string): string => { @@ -56,3 +60,7 @@ Vue.filter('humanizeTime', (value: string): string => { }) return formatDuration(durations) }) + +Vue.filter('capitalize', (value: string): string => { + return value.charAt(0).toUpperCase() + value.slice(1) +}) diff --git a/web/plugins/veutify.ts b/web/plugins/veutify.ts new file mode 100644 index 00000000..be8b07eb --- /dev/null +++ b/web/plugins/veutify.ts @@ -0,0 +1,67 @@ +import Vue from 'vue' +import { Route } from 'vue-router' +import { DataOptions } from 'vuetify' + +export type VForm = Vue & { + validate: () => boolean + resetValidation: () => boolean + reset: () => void +} + +export type FormInputType = string | null | File + +export type FormValidationRule = (value: FormInputType) => string | boolean + +export type FormValidationRules = Array + +export interface SelectItem { + text: string + value: string | number | boolean +} + +export interface DatatableFooterProps { + itemsPerPage: number + itemsPerPageOptions: Array +} + +export const DefaultFooterProps: DatatableFooterProps = { + itemsPerPage: 100, + itemsPerPageOptions: [10, 50, 100, 200], +} + +export type ParseParamsResponse = { + options: DataOptions + query: string | null +} + +export const parseFilterOptionsFromParams = ( + route: Route, + options: DataOptions, +): ParseParamsResponse => { + let query = null + Object.keys(route.query).forEach((value: string) => { + if (value === 'itemsPerPage') { + options.itemsPerPage = parseInt( + (route.query[value] as string) ?? options.itemsPerPage.toString(), + ) + } + + if (value === 'sortBy') { + options.sortBy = [(route.query[value] as string) ?? options.sortBy[0]] + } + + if (value === 'sortDesc') { + options.sortDesc = [!(route.query[value] === 'false')] + } + + if (value === 'page') { + options.page = parseInt( + (route.query[value] as string) ?? options.page.toString(), + ) + } + if (value === 'query') { + query = route.query[value] + } + }) + return { options, query } +} diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 6400b65d..17babf5d 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -1,4443 +1,11646 @@ -lockfileVersion: '6.0' +lockfileVersion: '9.0' settings: autoInstallPeers: true excludeLinksFromLockfile: false -dependencies: - '@mdi/js': - specifier: ^7.3.67 - version: 7.3.67 - '@nuxtjs/dotenv': - specifier: ^1.4.1 - version: 1.4.1 - '@nuxtjs/firebase': - specifier: ^8.2.2 - version: 8.2.2(@firebase/app-types@0.9.0)(firebase@9.23.0)(nuxt@2.17.2) - '@nuxtjs/sitemap': - specifier: ^2.4.0 - version: 2.4.0 - chart.js: - specifier: ^4.4.0 - version: 4.4.0 - chartjs-adapter-moment: - specifier: ^1.0.1 - version: 1.0.1(chart.js@4.4.0)(moment@2.29.4) - core-js: - specifier: ^3.33.2 - version: 3.33.2 - date-fns: - specifier: ^2.30.0 - version: 2.30.0 - dotenv: - specifier: ^16.3.1 - version: 16.3.1 - firebase: - specifier: ^9.23.0 - version: 9.23.0 - firebaseui: - specifier: ^6.1.0 - version: 6.1.0(firebase@9.23.0) - libphonenumber-js: - specifier: ^1.10.49 - version: 1.10.49 - moment: - specifier: ^2.29.4 - version: 2.29.4 - nuxt: - specifier: ^2.17.2 - version: 2.17.2(babel-core@7.0.0-bridge.0)(consola@3.2.3)(prettier@3.0.3)(typescript@4.9.5)(vue@2.7.15) - nuxt-highlightjs: - specifier: ^1.0.3 - version: 1.0.3 - ufo: - specifier: ^1.1.2 - version: 1.3.1 - vue: - specifier: ^2.7.15 - version: 2.7.15 - vue-chartjs: - specifier: ^5.2.0 - version: 5.2.0(chart.js@4.4.0)(vue@2.7.15) - vue-class-component: - specifier: ^7.2.6 - version: 7.2.6(vue@2.7.15) - vue-glow: - specifier: ^1.4.2 - version: 1.4.2 - vue-property-decorator: - specifier: ^9.1.2 - version: 9.1.2(vue-class-component@7.2.6)(vue@2.7.15) - vue-router: - specifier: ^3.6.5 - version: 3.6.5(vue@2.7.15) - vue-server-renderer: - specifier: 2.7.14 - version: 2.7.14 - vue-template-compiler: - specifier: ^2.7.15 - version: 2.7.15 - vuetify: - specifier: ^2.7.1 - version: 2.7.1(vue@2.7.15) - vuex: - specifier: ^3.6.2 - version: 3.6.2(vue@2.7.15) - webpack: - specifier: ^5.87.0 - version: 5.87.0 - -devDependencies: - '@babel/eslint-parser': - specifier: ^7.22.15 - version: 7.22.15(@babel/core@7.23.2)(eslint@8.53.0) - '@commitlint/cli': - specifier: ^17.7.2 - version: 17.7.2 - '@commitlint/config-conventional': - specifier: ^18.1.0 - version: 18.1.0 - '@nuxt/types': - specifier: ^2.17.0 - version: 2.17.0 - '@nuxt/typescript-build': - specifier: ^2.1.0 - version: 2.1.0(@nuxt/types@2.17.0)(eslint@8.53.0)(vue-template-compiler@2.7.15)(webpack@5.87.0) - '@nuxtjs/eslint-config-typescript': - specifier: ^12.1.0 - version: 12.1.0(eslint@8.53.0)(typescript@4.9.5) - '@nuxtjs/eslint-module': - specifier: ^4.1.0 - version: 4.1.0(eslint@8.53.0)(vite@4.4.9)(webpack@5.87.0) - '@nuxtjs/stylelint-module': - specifier: ^5.1.0 - version: 5.1.0(postcss@8.4.31)(stylelint@15.11.0)(vite@4.4.9)(webpack@5.87.0) - '@nuxtjs/vuetify': - specifier: ^1.12.3 - version: 1.12.3(vue@2.7.15)(webpack@5.87.0) - '@vue/test-utils': - specifier: ^1.3.6 - version: 1.3.6(vue-template-compiler@2.7.15)(vue@2.7.15) - axios: - specifier: ^0.25.0 - version: 0.25.0 - babel-core: - specifier: 7.0.0-bridge.0 - version: 7.0.0-bridge.0(@babel/core@7.23.2) - babel-jest: - specifier: ^29.7.0 - version: 29.7.0(@babel/core@7.23.2) - eslint: - specifier: ^8.53.0 - version: 8.53.0 - eslint-config-prettier: - specifier: ^9.0.0 - version: 9.0.0(eslint@8.53.0) - eslint-plugin-nuxt: - specifier: ^4.0.0 - version: 4.0.0(eslint@8.53.0) - eslint-plugin-vue: - specifier: ^9.16.1 - version: 9.16.1(eslint@8.53.0) - highlight.js: - specifier: ^11.8.0 - version: 11.8.0 - jest: - specifier: ^27.4.4 - version: 27.4.4(ts-node@10.9.1) - lint-staged: - specifier: ^14.0.1 - version: 14.0.1 - node-fetch-native: - specifier: ^1.4.1 - version: 1.4.1 - postcss-html: - specifier: ^1.5.0 - version: 1.5.0 - prettier: - specifier: 3.0.3 - version: 3.0.3 - stylelint: - specifier: ^15.11.0 - version: 15.11.0(typescript@4.9.5) - stylelint-config-prettier: - specifier: ^9.0.5 - version: 9.0.5(stylelint@15.11.0) - stylelint-config-recommended-vue: - specifier: ^1.5.0 - version: 1.5.0(postcss-html@1.5.0)(stylelint@15.11.0) - stylelint-config-standard: - specifier: ^34.0.0 - version: 34.0.0(stylelint@15.11.0) - ts-jest: - specifier: ^27.1.1 - version: 27.1.1(@babel/core@7.23.2)(babel-jest@29.7.0)(jest@27.4.4)(typescript@4.9.5) - vue-client-only: - specifier: ^2.1.0 - version: 2.1.0 - vue-jest: - specifier: ^3.0.4 - version: 3.0.4(babel-core@7.0.0-bridge.0)(vue-template-compiler@2.7.15)(vue@2.7.15) - vue-meta: - specifier: ^2.4.0 - version: 2.4.0 - vue-no-ssr: - specifier: ^1.1.1 - version: 1.1.1 +importers: + + .: + dependencies: + '@mdi/js': + specifier: ^7.4.47 + version: 7.4.47 + '@nuxtjs/dotenv': + specifier: ^1.4.2 + version: 1.4.2 + '@nuxtjs/firebase': + specifier: ^8.2.2 + version: 8.2.2(@firebase/app-types@0.9.2)(firebase@10.14.1)(nuxt@2.18.1(babel-core@7.0.0-bridge.0(@babel/core@7.28.4))(consola@3.2.3)(ejs@3.1.10)(handlebars@4.7.8)(prettier@3.8.1)(typescript@4.9.5)(vue@2.7.16)) + '@nuxtjs/sitemap': + specifier: ^2.4.0 + version: 2.4.0 + chart.js: + specifier: ^4.5.1 + version: 4.5.1 + chartjs-adapter-moment: + specifier: ^1.0.1 + version: 1.0.1(chart.js@4.5.1)(moment@2.30.1) + core-js: + specifier: ^3.48.0 + version: 3.48.0 + date-fns: + specifier: ^2.30.0 + version: 2.30.0 + dotenv: + specifier: ^17.2.3 + version: 17.2.3 + firebase: + specifier: ^10.14.1 + version: 10.14.1 + firebaseui: + specifier: ^6.1.0 + version: 6.1.0(firebase@10.14.1) + jest-environment-jsdom: + specifier: ^30.2.0 + version: 30.2.0 + libphonenumber-js: + specifier: ^1.12.36 + version: 1.12.36 + moment: + specifier: ^2.30.1 + version: 2.30.1 + nuxt: + specifier: ^2.18.1 + version: 2.18.1(babel-core@7.0.0-bridge.0(@babel/core@7.28.4))(consola@3.2.3)(ejs@3.1.10)(handlebars@4.7.8)(prettier@3.8.1)(typescript@4.9.5)(vue@2.7.16) + nuxt-highlightjs: + specifier: ^1.0.3 + version: 1.0.3 + pusher-js: + specifier: ^8.4.0 + version: 8.4.0 + qrcode: + specifier: ^1.5.0 + version: 1.5.4 + ufo: + specifier: ^1.6.1 + version: 1.6.1 + vue: + specifier: ^2.7.16 + version: 2.7.16 + vue-chartjs: + specifier: ^5.3.3 + version: 5.3.3(chart.js@4.5.1)(vue@2.7.16) + vue-class-component: + specifier: ^7.2.6 + version: 7.2.6(vue@2.7.16) + vue-glow: + specifier: ^1.4.2 + version: 1.4.2 + vue-property-decorator: + specifier: ^9.1.2 + version: 9.1.2(vue-class-component@7.2.6(vue@2.7.16))(vue@2.7.16) + vue-router: + specifier: ^3.6.5 + version: 3.6.5(vue@2.7.16) + vue-server-renderer: + specifier: 2.7.16 + version: 2.7.16 + vue-template-compiler: + specifier: ^2.7.16 + version: 2.7.16 + vuetify: + specifier: ^2.7.2 + version: 2.7.2(vue@2.7.16) + vuex: + specifier: ^3.6.2 + version: 3.6.2(vue@2.7.16) + webpack: + specifier: ^5.104.1 + version: 5.104.1 + devDependencies: + '@babel/eslint-parser': + specifier: ^7.28.6 + version: 7.28.6(@babel/core@7.28.4)(eslint@8.57.1) + '@commitlint/cli': + specifier: ^20.4.0 + version: 20.4.0(@types/node@25.1.0)(typescript@4.9.5) + '@commitlint/config-conventional': + specifier: ^20.4.0 + version: 20.4.0 + '@nuxt/types': + specifier: ^2.18.1 + version: 2.18.1 + '@nuxt/typescript-build': + specifier: ^3.0.2 + version: 3.0.2(@nuxt/types@2.18.1)(eslint@8.57.1)(typescript@4.9.5)(vue-template-compiler@2.7.16)(webpack@5.104.1) + '@nuxtjs/eslint-config-typescript': + specifier: ^12.1.0 + version: 12.1.0(eslint@8.57.1)(typescript@4.9.5) + '@nuxtjs/eslint-module': + specifier: ^4.1.0 + version: 4.1.0(eslint@8.57.1)(rollup@3.30.0)(vite@4.5.3(@types/node@25.1.0)(sass@1.32.13)(terser@5.44.1))(webpack@5.104.1) + '@nuxtjs/stylelint-module': + specifier: ^5.2.0 + version: 5.2.0(postcss@8.5.6)(rollup@3.30.0)(stylelint@15.11.0(typescript@4.9.5))(vite@4.5.3(@types/node@25.1.0)(sass@1.32.13)(terser@5.44.1))(webpack@5.104.1) + '@nuxtjs/vuetify': + specifier: ^1.12.3 + version: 1.12.3(vue@2.7.16)(webpack@5.104.1) + '@types/qrcode': + specifier: ^1.5.6 + version: 1.5.6 + '@vue/test-utils': + specifier: ^1.3.6 + version: 1.3.6(vue-template-compiler@2.7.16)(vue@2.7.16) + axios: + specifier: ^0.31.0 + version: 0.31.0 + babel-core: + specifier: 7.0.0-bridge.0 + version: 7.0.0-bridge.0(@babel/core@7.28.4) + babel-jest: + specifier: ^30.2.0 + version: 30.2.0(@babel/core@7.28.4) + eslint: + specifier: ^8.57.1 + version: 8.57.1 + eslint-config-prettier: + specifier: ^10.1.8 + version: 10.1.8(eslint@8.57.1) + eslint-plugin-nuxt: + specifier: ^4.0.0 + version: 4.0.0(eslint@8.57.1) + eslint-plugin-vue: + specifier: ^9.33.0 + version: 9.33.0(eslint@8.57.1) + highlight.js: + specifier: ^11.11.1 + version: 11.11.1 + jest: + specifier: ^30.2.0 + version: 30.2.0(@types/node@25.1.0) + lint-staged: + specifier: ^16.1.4 + version: 16.1.4 + node-fetch-native: + specifier: ^1.6.7 + version: 1.6.7 + postcss-html: + specifier: ^1.8.1 + version: 1.8.1 + prettier: + specifier: 3.8.1 + version: 3.8.1 + stylelint: + specifier: ^15.11.0 + version: 15.11.0(typescript@4.9.5) + stylelint-config-prettier: + specifier: ^9.0.5 + version: 9.0.5(stylelint@15.11.0(typescript@4.9.5)) + stylelint-config-recommended-vue: + specifier: ^1.5.0 + version: 1.5.0(postcss-html@1.8.1)(stylelint@15.11.0(typescript@4.9.5)) + stylelint-config-standard: + specifier: ^34.0.0 + version: 34.0.0(stylelint@15.11.0(typescript@4.9.5)) + ts-jest: + specifier: ^29.4.6 + version: 29.4.6(@babel/core@7.28.4)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.4))(jest-util@30.2.0)(jest@30.2.0(@types/node@25.1.0))(typescript@4.9.5) + vue-client-only: + specifier: ^2.1.0 + version: 2.1.0 + vue-jest: + specifier: ^3.0.7 + version: 3.0.7(babel-core@7.0.0-bridge.0(@babel/core@7.28.4))(vue-template-compiler@2.7.16)(vue@2.7.16) + vue-meta: + specifier: ^2.4.0 + version: 2.4.0 + vue-no-ssr: + specifier: ^1.1.1 + version: 1.1.1 packages: - /@aashutoshrathi/word-wrap@1.2.6: - resolution: - { - integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==, - } - engines: { node: '>=0.10.0' } - dev: true - - /@ampproject/remapping@2.2.1: - resolution: - { - integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==, - } - engines: { node: '>=6.0.0' } - dependencies: - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.19 - /@babel/code-frame@7.22.13: - resolution: - { - integrity: sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==, - } - engines: { node: '>=6.9.0' } - dependencies: - '@babel/highlight': 7.22.20 - chalk: 2.4.2 + '@aashutoshrathi/word-wrap@1.2.6': + resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==} + engines: {node: '>=0.10.0'} - /@babel/compat-data@7.23.2: - resolution: - { - integrity: sha512-0S9TQMmDHlqAZ2ITT95irXKfxN9bncq8ZCoJhun3nHL/lLUxd2NKBJYoNGWH7S0hz6fRQwWlAWn/ILM0C70KZQ==, - } - engines: { node: '>=6.9.0' } + '@ampproject/remapping@2.2.1': + resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} + engines: {node: '>=6.0.0'} - /@babel/core@7.23.0: - resolution: - { - integrity: sha512-97z/ju/Jy1rZmDxybphrBuI+jtJjFVoz7Mr9yUQVVVi+DNZE333uFQeMOqcCIy1x3WYBIbWftUSLmbNXNT7qFQ==, - } - engines: { node: '>=6.9.0' } - dependencies: - '@ampproject/remapping': 2.2.1 - '@babel/code-frame': 7.22.13 - '@babel/generator': 7.23.0 - '@babel/helper-compilation-targets': 7.22.15 - '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.0) - '@babel/helpers': 7.23.1 - '@babel/parser': 7.23.0 - '@babel/template': 7.22.15 - '@babel/traverse': 7.23.2 - '@babel/types': 7.23.0 - convert-source-map: 2.0.0 - debug: 4.3.4 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - dev: true + '@asamuzakjp/css-color@3.2.0': + resolution: {integrity: sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==} - /@babel/core@7.23.2: - resolution: - { - integrity: sha512-n7s51eWdaWZ3vGT2tD4T7J6eJs3QoBXydv7vkUM06Bf1cbVD2Kc2UrkzhiQwobfV7NwOnQXYL7UBJ5VPU+RGoQ==, - } - engines: { node: '>=6.9.0' } - dependencies: - '@ampproject/remapping': 2.2.1 - '@babel/code-frame': 7.22.13 - '@babel/generator': 7.23.0 - '@babel/helper-compilation-targets': 7.22.15 - '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.2) - '@babel/helpers': 7.23.2 - '@babel/parser': 7.23.0 - '@babel/template': 7.22.15 - '@babel/traverse': 7.23.2 - '@babel/types': 7.23.0 - convert-source-map: 2.0.0 - debug: 4.3.4 - gensync: 1.0.0-beta.2 - json5: 2.2.3 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color + '@babel/code-frame@7.22.13': + resolution: {integrity: sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==} + engines: {node: '>=6.9.0'} + + '@babel/code-frame@7.23.5': + resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} + engines: {node: '>=6.9.0'} + + '@babel/code-frame@7.24.7': + resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} + engines: {node: '>=6.9.0'} - /@babel/eslint-parser@7.22.15(@babel/core@7.23.2)(eslint@8.53.0): - resolution: - { - integrity: sha512-yc8OOBIQk1EcRrpizuARSQS0TWAcOMpEJ1aafhNznaeYkeL+OhqnDObGFylB8ka8VFF/sZc+S4RzHyO+3LjQxg==, - } - engines: { node: ^10.13.0 || ^12.13.0 || >=14.0.0 } + '@babel/code-frame@7.27.1': + resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.24.7': + resolution: {integrity: sha512-qJzAIcv03PyaWqxRgO4mSU3lihncDT296vnyuE2O8uA4w3UHWI4S3hgeZd1L8W1Bft40w9JxJ2b412iDUFFRhw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.28.4': + resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.24.7': + resolution: {integrity: sha512-nykK+LEK86ahTkX/3TgauT0ikKoNCfKHEaZYTUVupJdTLzGNvrblu4u6fa7DhZONAltdf8e662t/abY8idrd/g==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.28.4': + resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==} + engines: {node: '>=6.9.0'} + + '@babel/eslint-parser@7.28.6': + resolution: {integrity: sha512-QGmsKi2PBO/MHSQk+AAgA9R6OHQr+VqnniFE0eMWZcVcfBZoA2dKn2hUsl3Csg/Plt9opRUWdY7//VXsrIlEiA==} + engines: {node: ^10.13.0 || ^12.13.0 || >=14.0.0} peerDependencies: '@babel/core': ^7.11.0 - eslint: ^7.5.0 || ^8.0.0 - dependencies: - '@babel/core': 7.23.2 - '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1 - eslint: 8.53.0 - eslint-visitor-keys: 2.1.0 - semver: 6.3.1 - dev: true + eslint: ^7.5.0 || ^8.0.0 || ^9.0.0 - /@babel/generator@7.23.0: - resolution: - { - integrity: sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==, - } - engines: { node: '>=6.9.0' } - dependencies: - '@babel/types': 7.23.0 - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.19 - jsesc: 2.5.2 + '@babel/generator@7.24.7': + resolution: {integrity: sha512-oipXieGC3i45Y1A41t4tAqpnEZWgB/lC6Ehh6+rOviR5XWpTtMmLN+fGjz9vOiNRt0p6RtO6DtD0pdU3vpqdSA==} + engines: {node: '>=6.9.0'} - /@babel/helper-annotate-as-pure@7.22.5: - resolution: - { - integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==, - } - engines: { node: '>=6.9.0' } - dependencies: - '@babel/types': 7.23.0 - dev: false - - /@babel/helper-builder-binary-assignment-operator-visitor@7.22.15: - resolution: - { - integrity: sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==, - } - engines: { node: '>=6.9.0' } - dependencies: - '@babel/types': 7.23.0 - dev: false - - /@babel/helper-compilation-targets@7.22.15: - resolution: - { - integrity: sha512-y6EEzULok0Qvz8yyLkCvVX+02ic+By2UdOhylwUOvOn9dvYc9mKICJuuU1n1XBI02YWsNsnrY1kc6DVbjcXbtw==, - } - engines: { node: '>=6.9.0' } - dependencies: - '@babel/compat-data': 7.23.2 - '@babel/helper-validator-option': 7.22.15 - browserslist: 4.22.1 - lru-cache: 5.1.1 - semver: 6.3.1 + '@babel/generator@7.28.3': + resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.22.5': + resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-annotate-as-pure@7.24.7': + resolution: {integrity: sha512-BaDeOonYvhdKw+JoMVkAixAAJzG2jVPIwWoKBPdYuY9b452e2rPuI9QPYh3KpofZ3pW2akOmwZLOiOsHMiqRAg==} + engines: {node: '>=6.9.0'} - /@babel/helper-create-class-features-plugin@7.22.15(@babel/core@7.23.2): - resolution: - { - integrity: sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==, - } - engines: { node: '>=6.9.0' } + '@babel/helper-builder-binary-assignment-operator-visitor@7.24.7': + resolution: {integrity: sha512-xZeCVVdwb4MsDBkkyZ64tReWYrLRHlMN72vP7Bdm3OUOuyFZExhsHUUnuWnm2/XOlAJzR0LfPpB56WXZn0X/lA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.24.7': + resolution: {integrity: sha512-ctSdRHBi20qWOfy27RUb4Fhp07KSJ3sXcuSvTrXrc4aG8NSYDo1ici3Vhg9bg69y5bj0Mr1lh0aeEgTvc12rMg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.27.2': + resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-create-class-features-plugin@7.24.5': + resolution: {integrity: sha512-uRc4Cv8UQWnE4NXlYTIIdM7wfFkOqlFztcC/gVXDKohKoVB3OyonfelUBaJzSwpBntZ2KYGF/9S7asCHsXwW6g==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.23.2 - '@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.23.0 - '@babel/helper-optimise-call-expression': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.2) - '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - semver: 6.3.1 - dev: false - /@babel/helper-create-regexp-features-plugin@7.22.15(@babel/core@7.23.2): - resolution: - { - integrity: sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==, - } - engines: { node: '>=6.9.0' } + '@babel/helper-create-class-features-plugin@7.24.7': + resolution: {integrity: sha512-kTkaDl7c9vO80zeX1rJxnuRpEsD5tA81yh11X1gQo+PhSti3JS+7qeZo9U4RHobKRiFPKaGK3svUAeb8D0Q7eg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-create-regexp-features-plugin@7.22.15': + resolution: {integrity: sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-create-regexp-features-plugin@7.24.7': + resolution: {integrity: sha512-03TCmXy2FtXJEZfbXDTSqq1fRJArk7lX9DOFC/47VthYcxyIOx+eXQmdo6DOQvrbpIix+KfXwvuXdFDZHxt+rA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-annotate-as-pure': 7.22.5 - regexpu-core: 5.3.2 - semver: 6.3.1 - dev: false - /@babel/helper-define-polyfill-provider@0.4.3(@babel/core@7.23.2): - resolution: - { - integrity: sha512-WBrLmuPP47n7PNwsZ57pqam6G/RGo1vw/87b0Blc53tZNGZ4x7YvZ6HgQe2vo1W/FR20OgjeZuGXzudPiXHFug==, - } + '@babel/helper-define-polyfill-provider@0.6.2': + resolution: {integrity: sha512-LV76g+C502biUK6AyZ3LK10vDpDyCzZnhZFXkH1L75zHPj68+qc8Zfpx2th+gzwA2MzyK+1g/3EPl62yFnVttQ==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-compilation-targets': 7.22.15 - '@babel/helper-plugin-utils': 7.22.5 - debug: 4.3.4 - lodash.debounce: 4.0.8 - resolve: 1.22.6 - transitivePeerDependencies: - - supports-color - dev: false - - /@babel/helper-environment-visitor@7.22.20: - resolution: - { - integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==, - } - engines: { node: '>=6.9.0' } - - /@babel/helper-function-name@7.23.0: - resolution: - { - integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==, - } - engines: { node: '>=6.9.0' } - dependencies: - '@babel/template': 7.22.15 - '@babel/types': 7.23.0 - - /@babel/helper-hoist-variables@7.22.5: - resolution: - { - integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==, - } - engines: { node: '>=6.9.0' } - dependencies: - '@babel/types': 7.23.0 - - /@babel/helper-member-expression-to-functions@7.23.0: - resolution: - { - integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==, - } - engines: { node: '>=6.9.0' } - dependencies: - '@babel/types': 7.23.0 - dev: false - - /@babel/helper-module-imports@7.22.15: - resolution: - { - integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==, - } - engines: { node: '>=6.9.0' } - dependencies: - '@babel/types': 7.23.0 - - /@babel/helper-module-transforms@7.23.0(@babel/core@7.23.0): - resolution: - { - integrity: sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==, - } - engines: { node: '>=6.9.0' } + + '@babel/helper-environment-visitor@7.22.20': + resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-environment-visitor@7.24.7': + resolution: {integrity: sha512-DoiN84+4Gnd0ncbBOM9AZENV4a5ZiL39HYMyZJGZ/AZEykHYdJw0wW3kdcsh9/Kn+BRXHLkkklZ51ecPKmI1CQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-function-name@7.23.0': + resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-function-name@7.24.7': + resolution: {integrity: sha512-FyoJTsj/PEUWu1/TYRiXTIHc8lbw+TDYkZuoE43opPS5TrI7MyONBE1oNvfguEXAD9yhQRrVBnXdXzSLQl9XnA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-hoist-variables@7.24.7': + resolution: {integrity: sha512-MJJwhkoGy5c4ehfoRyrJ/owKeMl19U54h27YYftT0o2teQ3FJ3nQUf/I3LlJsX4l3qlw7WRXUmiyajvHXoTubQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.24.5': + resolution: {integrity: sha512-4owRteeihKWKamtqg4JmWSsEZU445xpFRXPEwp44HbgbxdWlUV1b4Agg4lkA806Lil5XM/e+FJyS0vj5T6vmcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-member-expression-to-functions@7.24.7': + resolution: {integrity: sha512-LGeMaf5JN4hAT471eJdBs/GK1DoYIJ5GCtZN/EsL6KUiiDZOvO/eKE11AMZJa2zP4zk4qe9V2O/hxAmkRc8p6w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.24.7': + resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.27.1': + resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.24.7': + resolution: {integrity: sha512-1fuJEwIrp+97rM4RWdO+qrRsZlAeL1lQJoPqtCYWv0NL115XM93hIH4CSRln2w52SqvmY5hqdtauB6QFCDiZNQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.23.0 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-module-imports': 7.22.15 - '@babel/helper-simple-access': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/helper-validator-identifier': 7.22.20 - dev: true - - /@babel/helper-module-transforms@7.23.0(@babel/core@7.23.2): - resolution: - { - integrity: sha512-WhDWw1tdrlT0gMgUJSlX0IQvoO1eN279zrAUbVB+KpV2c3Tylz8+GnKOLllCS6Z/iZQEyVYxhZVUdPTqs2YYPw==, - } - engines: { node: '>=6.9.0' } + + '@babel/helper-module-transforms@7.28.3': + resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-module-imports': 7.22.15 - '@babel/helper-simple-access': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/helper-validator-identifier': 7.22.20 - - /@babel/helper-optimise-call-expression@7.22.5: - resolution: - { - integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==, - } - engines: { node: '>=6.9.0' } - dependencies: - '@babel/types': 7.23.0 - dev: false - - /@babel/helper-plugin-utils@7.22.5: - resolution: - { - integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==, - } - engines: { node: '>=6.9.0' } - - /@babel/helper-remap-async-to-generator@7.22.20(@babel/core@7.23.2): - resolution: - { - integrity: sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==, - } - engines: { node: '>=6.9.0' } + + '@babel/helper-optimise-call-expression@7.22.5': + resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-optimise-call-expression@7.24.7': + resolution: {integrity: sha512-jKiTsW2xmWwxT1ixIdfXUZp+P5yURx2suzLZr5Hi64rURpDYdMW0pv+Uf17EYk2Rd428Lx4tLsnjGJzYKDM/6A==} + engines: {node: '>=6.9.0'} + + '@babel/helper-plugin-utils@7.27.1': + resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-remap-async-to-generator@7.24.7': + resolution: {integrity: sha512-9pKLcTlZ92hNZMQfGCHImUpDOlAgkkpqalWEeftW5FBya75k8Li2ilerxkM/uBEj01iBZXcCIB/bwvDYgWyibA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-wrap-function': 7.22.20 - dev: false - /@babel/helper-replace-supers@7.22.20(@babel/core@7.23.2): - resolution: - { - integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==, - } - engines: { node: '>=6.9.0' } + '@babel/helper-replace-supers@7.24.1': + resolution: {integrity: sha512-QCR1UqC9BzG5vZl8BMicmZ28RuUBnHhAMddD8yHFHDRH9lLTZ9uUPehX8ctVPT8l0TKblJidqcgUUKGVrePleQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-member-expression-to-functions': 7.23.0 - '@babel/helper-optimise-call-expression': 7.22.5 - dev: false - - /@babel/helper-simple-access@7.22.5: - resolution: - { - integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==, - } - engines: { node: '>=6.9.0' } - dependencies: - '@babel/types': 7.23.0 - - /@babel/helper-skip-transparent-expression-wrappers@7.22.5: - resolution: - { - integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==, - } - engines: { node: '>=6.9.0' } - dependencies: - '@babel/types': 7.23.0 - dev: false - - /@babel/helper-split-export-declaration@7.22.6: - resolution: - { - integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==, - } - engines: { node: '>=6.9.0' } - dependencies: - '@babel/types': 7.23.0 - - /@babel/helper-string-parser@7.22.5: - resolution: - { - integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==, - } - engines: { node: '>=6.9.0' } - - /@babel/helper-validator-identifier@7.22.20: - resolution: - { - integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==, - } - engines: { node: '>=6.9.0' } - - /@babel/helper-validator-option@7.22.15: - resolution: - { - integrity: sha512-bMn7RmyFjY/mdECUbgn9eoSY4vqvacUnS9i9vGAGttgFWesO6B4CYWA7XlpbWgBt71iv/hfbPlynohStqnu5hA==, - } - engines: { node: '>=6.9.0' } - - /@babel/helper-wrap-function@7.22.20: - resolution: - { - integrity: sha512-pms/UwkOpnQe/PDAEdV/d7dVCoBbB+R4FvYoHGZz+4VPcg7RtYy2KP7S2lbuWM6FCSgob5wshfGESbC/hzNXZw==, - } - engines: { node: '>=6.9.0' } - dependencies: - '@babel/helper-function-name': 7.23.0 - '@babel/template': 7.22.15 - '@babel/types': 7.23.0 - dev: false - - /@babel/helpers@7.23.1: - resolution: - { - integrity: sha512-chNpneuK18yW5Oxsr+t553UZzzAs3aZnFm4bxhebsNTeshrC95yA7l5yl7GBAG+JG1rF0F7zzD2EixK9mWSDoA==, - } - engines: { node: '>=6.9.0' } - dependencies: - '@babel/template': 7.22.15 - '@babel/traverse': 7.23.2 - '@babel/types': 7.23.0 - transitivePeerDependencies: - - supports-color - dev: true - - /@babel/helpers@7.23.2: - resolution: - { - integrity: sha512-lzchcp8SjTSVe/fPmLwtWVBFC7+Tbn8LGHDVfDp9JGxpAY5opSaEFgt8UQvrnECWOTdji2mOWMz1rOhkHscmGQ==, - } - engines: { node: '>=6.9.0' } - dependencies: - '@babel/template': 7.22.15 - '@babel/traverse': 7.23.2 - '@babel/types': 7.23.0 - transitivePeerDependencies: - - supports-color - /@babel/highlight@7.22.20: - resolution: - { - integrity: sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==, - } - engines: { node: '>=6.9.0' } - dependencies: - '@babel/helper-validator-identifier': 7.22.20 - chalk: 2.4.2 - js-tokens: 4.0.0 + '@babel/helper-replace-supers@7.24.7': + resolution: {integrity: sha512-qTAxxBM81VEyoAY0TtLrx1oAEJc09ZK67Q9ljQToqCnA+55eNwCORaxlKyu+rNfX86o8OXRUSNUnrtsAZXM9sg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-simple-access@7.24.7': + resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} + engines: {node: '>=6.9.0'} - /@babel/parser@7.23.0: - resolution: - { - integrity: sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==, - } - engines: { node: '>=6.0.0' } + '@babel/helper-skip-transparent-expression-wrappers@7.22.5': + resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-skip-transparent-expression-wrappers@7.24.7': + resolution: {integrity: sha512-IO+DLT3LQUElMbpzlatRASEyQtfhSE0+m465v++3jyyXeBTBUjtVZg28/gHeV5mrTJqvEKhKroBGAvhW+qPHiQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-split-export-declaration@7.24.5': + resolution: {integrity: sha512-5CHncttXohrHk8GWOFCcCl4oRD9fKosWlIRgWm4ql9VYioKm52Mk2xsmoohvm7f3JoiLSM5ZgJuRaf5QZZYd3Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-split-export-declaration@7.24.7': + resolution: {integrity: sha512-oy5V7pD+UvfkEATUKvIjvIAH/xCzfsFVw7ygW2SI6NClZzquT+mwdTfgfdbUiceh6iQO0CHtCPsyze/MZ2YbAA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.24.5': + resolution: {integrity: sha512-3q93SSKX2TWCG30M2G2kwaKeTYgEUp5Snjuj8qm729SObL6nbtUldAi37qbxkD5gg3xnBio+f9nqpSepGZMvxA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.27.1': + resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.24.7': + resolution: {integrity: sha512-yy1/KvjhV/ZCL+SM7hBrvnZJ3ZuT9OuZgIJAGpPEToANvc3iM6iDvBnRjtElWibHU6n8/LPR/EjX9EtIEYO3pw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-wrap-function@7.24.7': + resolution: {integrity: sha512-N9JIYk3TD+1vq/wn77YnJOqMtfWhNewNE+DJV4puD2X7Ew9J4JvrzrFDfTfyv5EgEXVy9/Wt8QiOErzEmv5Ifw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.24.7': + resolution: {integrity: sha512-NlmJJtvcw72yRJRcnCmGvSi+3jDEg8qFu3z0AFoymmzLx5ERVWyzd9kVXr7Th9/8yIJi2Zc6av4Tqz3wFs8QWg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.28.4': + resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==} + engines: {node: '>=6.9.0'} + + '@babel/highlight@7.23.4': + resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} + engines: {node: '>=6.9.0'} + + '@babel/highlight@7.24.7': + resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.24.0': + resolution: {integrity: sha512-QuP/FxEAzMSjXygs8v4N9dvdXzEHN4W1oF3PxuWAtPo08UdM17u89RDMgjLn/mlc56iM0HlLmVkO/wgR+rDgHg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/parser@7.28.0': + resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/parser@7.28.4': + resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==} + engines: {node: '>=6.0.0'} hasBin: true - dependencies: - '@babel/types': 7.23.0 - /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.22.15(@babel/core@7.23.2): - resolution: - { - integrity: sha512-FB9iYlz7rURmRJyXRKEnalYPPdn87H5no108cyuQQyMwlpJ2SJtpIUBI27kdTin956pz+LPypkPVPUTlxOmrsg==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.7': + resolution: {integrity: sha512-TiT1ss81W80eQsN+722OaeQMY/G4yTb4G9JrqeiDADs3N8lbPMGldWi9x8tyqCW5NLx1Jh2AvkE6r6QvEltMMQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.22.15(@babel/core@7.23.2): - resolution: - { - integrity: sha512-Hyph9LseGvAeeXzikV88bczhsrLrIZqDPxO+sSmAunMPaGrBGhfMWzCPYTtiW9t+HzSE2wtV8e5cc5P6r1xMDQ==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.7': + resolution: {integrity: sha512-unaQgZ/iRu/By6tsjMZzpeBZjChYfLYry6HrEXPoz3KmfF0sVBQ1l8zKMQ4xRGLWVsjuvB8nQfjNP/DcfEOCsg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.7': + resolution: {integrity: sha512-+izXIbke1T33mY4MSNnrqhPXDz01WYhEf3yF5NbnUtkiNnm+XBZJl3kNfoK6NKmYlz/D07+l2GWVK/QfDkNCuQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.13.0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - '@babel/plugin-transform-optional-chaining': 7.23.0(@babel/core@7.23.2) - dev: false - - /@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.23.2): - resolution: - { - integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==, - } - engines: { node: '>=6.9.0' } + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.7': + resolution: {integrity: sha512-utA4HuR6F4Vvcr+o4DnjL8fCOlgRFGbeeBEGNg3ZTrLFw6VWG5XmUrvcQ0FjIYMU2ST4XcR2Wsp7t9qOAPnxMg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/plugin-proposal-class-properties@7.18.6': + resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==} + engines: {node: '>=6.9.0'} deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead. peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.2) - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-proposal-decorators@7.23.2(@babel/core@7.23.2): - resolution: - { - integrity: sha512-eR0gJQc830fJVGz37oKLvt9W9uUIQSAovUl0e9sJ3YeO09dlcoBVYD3CLrjCj4qHdXmfiyTyFt8yeQYSN5fxLg==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-proposal-decorators@7.24.7': + resolution: {integrity: sha512-RL9GR0pUG5Kc8BUWLNDm2T5OpYwSX15r98I0IkgmRQTXuELq/OynH8xtMTMvTJFjXbMWFVTKtYkTaYQsuAwQlQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.2) - '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.2) - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/plugin-syntax-decorators': 7.22.10(@babel/core@7.23.2) - dev: false - - /@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.23.2): - resolution: - { - integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==, - } - engines: { node: '>=6.9.0' } + + '@babel/plugin-proposal-nullish-coalescing-operator@7.18.6': + resolution: {integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==} + engines: {node: '>=6.9.0'} deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-nullish-coalescing-operator instead. peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.2) - dev: false - - /@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.23.2): - resolution: - { - integrity: sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==, - } - engines: { node: '>=6.9.0' } + + '@babel/plugin-proposal-optional-chaining@7.21.0': + resolution: {integrity: sha512-p4zeefM72gpmEe2fkUr/OnOXpWEf8nAgk7ZYVqqfFiyIG7oFfVZcCrU64hWn5xp4tQ9LkV4bTIa5rD0KANpKNA==} + engines: {node: '>=6.9.0'} deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-optional-chaining instead. peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.2) - dev: false - - /@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.23.2): - resolution: - { - integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==, - } - engines: { node: '>=6.9.0' } + + '@babel/plugin-proposal-private-methods@7.18.6': + resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==} + engines: {node: '>=6.9.0'} deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-methods instead. peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.2) - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.2): - resolution: - { - integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2': + resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - dev: false - /@babel/plugin-proposal-private-property-in-object@7.21.11(@babel/core@7.23.2): - resolution: - { - integrity: sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-proposal-private-property-in-object@7.21.11': + resolution: {integrity: sha512-0QZ8qP/3RLDVBwBFoWAwCtgcDZJVwA5LUJRZU8x2YFfKNuFq161wK3cuGrALu5yiPu+vzwTAg/sMWVNeWeNyaw==} + engines: {node: '>=6.9.0'} deprecated: This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-private-property-in-object instead. peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.2) - '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.2) - dev: false - /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.0): - resolution: - { - integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==, - } + '@babel/plugin-syntax-async-generators@7.8.4': + resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.2): - resolution: - { - integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==, - } + '@babel/plugin-syntax-bigint@7.8.3': + resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.0): - resolution: - { - integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==, - } + '@babel/plugin-syntax-class-properties@7.12.13': + resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.23.2): - resolution: - { - integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==, - } + '@babel/plugin-syntax-class-static-block@7.14.5': + resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.0): - resolution: - { - integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==, - } + '@babel/plugin-syntax-decorators@7.24.7': + resolution: {integrity: sha512-Ui4uLJJrRV1lb38zg1yYTmRKmiZLiftDEvZN2iq3kd9kUFU+PttmzTbAFC2ucRk/XJmtek6G23gPsuZbhrT8fQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.2): - resolution: - { - integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==, - } + '@babel/plugin-syntax-dynamic-import@7.8.3': + resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-syntax-export-namespace-from@7.8.3': + resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-syntax-decorators@7.22.10(@babel/core@7.23.2): - resolution: - { - integrity: sha512-z1KTVemBjnz+kSEilAsI4lbkPOl5TvJH7YDSY1CTIzvLWJ+KHXp+mRe8VPmfnyvqOPqar1V2gid2PleKzRUstQ==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-syntax-import-assertions@7.24.7': + resolution: {integrity: sha512-Ec3NRUMoi8gskrkBe3fNmEQfxDvY8bgfQpz6jlk/41kX9eUjvpyqWU7PBP/pLAvMaSQjbMNKJmvX57jP+M6bPg==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.23.2): - resolution: - { - integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==, - } + '@babel/plugin-syntax-import-attributes@7.24.7': + resolution: {integrity: sha512-hbX+lKKeUMGihnK8nvKqmXBInriT3GVjzXKFriV3YC6APGxMbP8RZNFwy91+hocLXq90Mta+HshoB31802bb8A==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.23.2): - resolution: - { - integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==, - } + '@babel/plugin-syntax-import-attributes@7.27.1': + resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-syntax-import-assertions@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-rdV97N7KqsRzeNGoWUOK6yUsWarLjE5Su/Snk9IYPU9CwkWHs4t+rTGOvffTR8XGkJMTAdLfO0xVnXm8wugIJg==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-syntax-import-meta@7.10.4': + resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-syntax-import-attributes@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-KwvoWDeNKPETmozyFE0P2rOLqh39EoQHNjqizrI5B8Vt0ZNS7M56s7dAiAqbYfiAYOuIzIh96z3iR2ktgu3tEg==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-syntax-json-strings@7.8.3': + resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.0): - resolution: - { - integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==, - } + '@babel/plugin-syntax-jsx@7.27.1': + resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.2): - resolution: - { - integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==, - } + '@babel/plugin-syntax-logical-assignment-operators@7.10.4': + resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.0): - resolution: - { - integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==, - } + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3': + resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.2): - resolution: - { - integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==, - } + '@babel/plugin-syntax-numeric-separator@7.10.4': + resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-syntax-object-rest-spread@7.8.3': + resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.0): - resolution: - { - integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==, - } + '@babel/plugin-syntax-optional-catch-binding@7.8.3': + resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.2): - resolution: - { - integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==, - } + '@babel/plugin-syntax-optional-chaining@7.8.3': + resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.0): - resolution: - { - integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==, - } + '@babel/plugin-syntax-private-property-in-object@7.14.5': + resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.2): - resolution: - { - integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==, - } + '@babel/plugin-syntax-top-level-await@7.14.5': + resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.0): - resolution: - { - integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==, - } + '@babel/plugin-syntax-typescript@7.27.1': + resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.2): - resolution: - { - integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==, - } + '@babel/plugin-syntax-unicode-sets-regex@7.18.6': + resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==} + engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 + '@babel/core': ^7.0.0 - /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.0): - resolution: - { - integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==, - } + '@babel/plugin-transform-arrow-functions@7.24.7': + resolution: {integrity: sha512-Dt9LQs6iEY++gXUwY03DNFat5C2NbO48jj+j/bSAz6b3HgPs39qcPiYt77fDObIcFwj3/C2ICX9YMwGflUoSHQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.2): - resolution: - { - integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==, - } + '@babel/plugin-transform-async-generator-functions@7.24.7': + resolution: {integrity: sha512-o+iF77e3u7ZS4AoAuJvapz9Fm001PuD2V3Lp6OSE4FYQke+cSewYtnek+THqGRWyQloRCyvWL1OkyfNEl9vr/g==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.0): - resolution: - { - integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==, - } + '@babel/plugin-transform-async-to-generator@7.24.7': + resolution: {integrity: sha512-SQY01PcJfmQ+4Ash7NE+rpbLFbmqA2GPIgqzxfFTL4t1FKRq4zTms/7htKpoCUI9OcFYgzqfmCdH53s6/jn5fA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.2): - resolution: - { - integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==, - } + '@babel/plugin-transform-block-scoped-functions@7.24.7': + resolution: {integrity: sha512-yO7RAz6EsVQDaBH18IDJcMB1HnrUn2FJ/Jslc/WtPPWcjhpUJXU/rjbwmluzp7v/ZzWcEhTMXELnnsz8djWDwQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.0): - resolution: - { - integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==, - } + '@babel/plugin-transform-block-scoping@7.24.7': + resolution: {integrity: sha512-Nd5CvgMbWc+oWzBsuaMcbwjJWAcp5qzrbg69SZdHSP7AMY0AbWFqFO0WTFCA1jxhMCwodRwvRec8k0QUbZk7RQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.2): - resolution: - { - integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==, - } + '@babel/plugin-transform-class-properties@7.24.7': + resolution: {integrity: sha512-vKbfawVYayKcSeSR5YYzzyXvsDFWU2mD8U5TFeXtbCPLFUqe7GyCgvO6XDHzje862ODrOwy6WCPmKeWHbCFJ4w==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-class-static-block@7.24.7': + resolution: {integrity: sha512-HMXK3WbBPpZQufbMG4B46A90PkuuhN9vBCb5T8+VAHqvAqvcLi+2cKoukcpmUYkszLhScU3l1iudhrks3DggRQ==} + engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: false + '@babel/core': ^7.12.0 - /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.0): - resolution: - { - integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-classes@7.24.7': + resolution: {integrity: sha512-CFbbBigp8ln4FU6Bpy6g7sE8B/WmCmzvivzUC6xDAdWVsjYTXijpuuGJmYkAaoWAzcItGKT3IOAbxRItZ5HTjw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-computed-properties@7.24.7': + resolution: {integrity: sha512-25cS7v+707Gu6Ds2oY6tCkUwsJ9YIDbggd9+cu9jzzDgiNq7hR/8dkzxWfKWnTic26vsI3EsCXNd4iEB6e8esQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - /@babel/plugin-syntax-typescript@7.22.5(@babel/core@7.23.0): - resolution: - { - integrity: sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-destructuring@7.24.7': + resolution: {integrity: sha512-19eJO/8kdCQ9zISOf+SEUJM/bAUIsvY3YDnXZTupUCQ8LgrWnsG/gFB9dvXqdXnRXMAM8fvt7b0CBKQHNGy1mw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: true - /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.23.2): - resolution: - { - integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-dotall-regex@7.24.7': + resolution: {integrity: sha512-ZOA3W+1RRTSWvyqcMJDLqbchh7U4NRGqwRfFSVbOLS/ePIP4vHB5e8T8eXcuqyN1QkgKyj5wuW0lcS85v4CrSw==} + engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.2) - '@babel/helper-plugin-utils': 7.22.5 - dev: false + '@babel/core': ^7.0.0-0 - /@babel/plugin-transform-arrow-functions@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-26lTNXoVRdAnsaDXPpvCNUq+OVWEVC6bx7Vvz9rC53F2bagUWW4u4ii2+h8Fejfh7RYqPxn+libeFBBck9muEw==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-duplicate-keys@7.24.7': + resolution: {integrity: sha512-JdYfXyCRihAe46jUIliuL2/s0x0wObgwwiGxw/UbgJBr20gQBThrokO4nYKgWkD7uBaqM7+9x5TU7NkExZJyzw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-transform-async-generator-functions@7.23.2(@babel/core@7.23.2): - resolution: - { - integrity: sha512-BBYVGxbDVHfoeXbOwcagAkOQAm9NxoTdMGfTqghu1GrvadSaw6iW3Je6IcL5PNOw8VwjxqBECXy50/iCQSY/lQ==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-dynamic-import@7.24.7': + resolution: {integrity: sha512-sc3X26PhZQDb3JhORmakcbvkeInvxz+A8oda99lj7J60QRuPZvNAk9wQlTBS1ZynelDrDmTU4pw1tyc5d5ZMUg==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.2) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.2) - dev: false - - /@babel/plugin-transform-async-to-generator@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-b1A8D8ZzE/VhNDoV1MSJTnpKkCG5bJo+19R4o4oy03zM7ws8yEMK755j61Dc3EyvdysbqH5BOOTquJ7ZX9C6vQ==, - } - engines: { node: '>=6.9.0' } + + '@babel/plugin-transform-exponentiation-operator@7.24.7': + resolution: {integrity: sha512-Rqe/vSc9OYgDajNIK35u7ot+KeCoetqQYFXM4Epf7M7ez3lWlOjrDjrwMei6caCVhfdw+mIKD4cgdGNy5JQotQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-module-imports': 7.22.15 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-remap-async-to-generator': 7.22.20(@babel/core@7.23.2) - dev: false - /@babel/plugin-transform-block-scoped-functions@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-tdXZ2UdknEKQWKJP1KMNmuF5Lx3MymtMN/pvA+p/VEkhK8jVcQ1fzSy8KM9qRYhAf2/lV33hoMPKI/xaI9sADA==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-export-namespace-from@7.24.7': + resolution: {integrity: sha512-v0K9uNYsPL3oXZ/7F9NNIbAj2jv1whUEtyA6aujhekLs56R++JDQuzRcP2/z4WX5Vg/c5lE9uWZA0/iUoFhLTA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-transform-block-scoping@7.23.0(@babel/core@7.23.2): - resolution: - { - integrity: sha512-cOsrbmIOXmf+5YbL99/S49Y3j46k/T16b9ml8bm9lP6N9US5iQ2yBK7gpui1pg0V/WMcXdkfKbTb7HXq9u+v4g==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-for-of@7.24.7': + resolution: {integrity: sha512-wo9ogrDG1ITTTBsy46oGiN1dS9A7MROBTcYsfS8DtsImMkHk9JXJ3EWQM6X2SUw4x80uGPlwj0o00Uoc6nEE3g==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-transform-class-properties@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-nDkQ0NfkOhPTq8YCLiWNxp1+f9fCobEjCb0n8WdbNUBc4IB5V7P1QnX9IjpSoquKrXF5SKojHleVNs2vGeHCHQ==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-function-name@7.24.7': + resolution: {integrity: sha512-U9FcnA821YoILngSmYkW6FjyQe2TyZD5pHt4EVIhmcTkrJw/3KqcrRSxuOo5tFZJi7TE19iDyI1u+weTI7bn2w==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.2) - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-transform-class-static-block@7.22.11(@babel/core@7.23.2): - resolution: - { - integrity: sha512-GMM8gGmqI7guS/llMFk1bJDkKfn3v3C4KHK9Yg1ey5qcHcOlKb0QvcMrgzvxo+T03/4szNh5lghY+fEC98Kq9g==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-json-strings@7.24.7': + resolution: {integrity: sha512-2yFnBGDvRuxAaE/f0vfBKvtnvvqU8tGpMHqMNpTN2oWMKIR3NqFkjaAgGwawhqK/pIN2T3XdjGPdaG0vDhOBGw==} + engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.12.0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.2) - '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.2) - dev: false + '@babel/core': ^7.0.0-0 - /@babel/plugin-transform-classes@7.22.15(@babel/core@7.23.2): - resolution: - { - integrity: sha512-VbbC3PGjBdE0wAWDdHM9G8Gm977pnYI0XpqMd6LrKISj8/DJXEsWqgRuTYaNE9Bv0JGhTZUzHDlMk18IpOuoqw==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-literals@7.24.7': + resolution: {integrity: sha512-vcwCbb4HDH+hWi8Pqenwnjy+UiklO4Kt1vfspcQYFhJdpthSnW8XvWGyDZWKNVrVbVViI/S7K9PDJZiUmP2fYQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-compilation-targets': 7.22.15 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-function-name': 7.23.0 - '@babel/helper-optimise-call-expression': 7.22.5 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.2) - '@babel/helper-split-export-declaration': 7.22.6 - globals: 11.12.0 - dev: false - /@babel/plugin-transform-computed-properties@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-4GHWBgRf0krxPX+AaPtgBAlTgTeZmqDynokHOX7aqqAB4tHs3U2Y02zH6ETFdLZGcg9UQSD1WCmkVrE9ErHeOg==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-logical-assignment-operators@7.24.7': + resolution: {integrity: sha512-4D2tpwlQ1odXmTEIFWy9ELJcZHqrStlzK/dAOWYyxX3zT0iXQB6banjgeOJQXzEc4S0E0a5A+hahxPaEFYftsw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/template': 7.22.15 - dev: false - /@babel/plugin-transform-destructuring@7.23.0(@babel/core@7.23.2): - resolution: - { - integrity: sha512-vaMdgNXFkYrB+8lbgniSYWHsgqK5gjaMNcc84bMIOMRLH0L9AqYq3hwMdvnyqj1OPqea8UtjPEuS/DCenah1wg==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-member-expression-literals@7.24.7': + resolution: {integrity: sha512-T/hRC1uqrzXMKLQ6UCwMT85S3EvqaBXDGf0FaMf4446Qx9vKwlghvee0+uuZcDUCZU5RuNi4781UQ7R308zzBw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-transform-dotall-regex@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-modules-amd@7.24.7': + resolution: {integrity: sha512-9+pB1qxV3vs/8Hdmz/CulFB8w2tuu6EB94JZFsjdqxQokwGa9Unap7Bo2gGBGIvPmDIVvQrom7r5m/TCDMURhg==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.2) - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-transform-duplicate-keys@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-dEnYD+9BBgld5VBXHnF/DbYGp3fqGMsyxKbtD1mDyIA7AkTSpKXFhCVuj/oQVOoALfBs77DudA0BE4d5mcpmqw==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-modules-commonjs@7.24.7': + resolution: {integrity: sha512-iFI8GDxtevHJ/Z22J5xQpVqFLlMNstcLXh994xifFwxxGslr2ZXXLWgtBeLctOD63UFDArdvN6Tg8RFw+aEmjQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-transform-dynamic-import@7.22.11(@babel/core@7.23.2): - resolution: - { - integrity: sha512-g/21plo58sfteWjaO0ZNVb+uEOkJNjAaHhbejrnBmu011l/eNDScmkbjCC3l4FKb10ViaGU4aOkFznSu2zRHgA==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-modules-systemjs@7.24.7': + resolution: {integrity: sha512-GYQE0tW7YoaN13qFh3O1NCY4MPkUiAH3fiF7UcV/I3ajmDKEdG3l+UOcbAm4zUE3gnvUU+Eni7XrVKo9eO9auw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.2) - dev: false - /@babel/plugin-transform-exponentiation-operator@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-vIpJFNM/FjZ4rh1myqIya9jXwrwwgFRHPjT3DkUA9ZLHuzox8jiXkOLvwm1H+PQIP3CqfC++WPKeuDi0Sjdj1g==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-modules-umd@7.24.7': + resolution: {integrity: sha512-3aytQvqJ/h9z4g8AsKPLvD4Zqi2qT+L3j7XoFFu1XBlZWEl2/1kWnhmAbxpLgPrHSY0M6UA02jyTiwUVtiKR6A==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-builder-binary-assignment-operator-visitor': 7.22.15 - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-transform-export-namespace-from@7.22.11(@babel/core@7.23.2): - resolution: - { - integrity: sha512-xa7aad7q7OiT8oNZ1mU7NrISjlSkVdMbNxn9IuLZyL9AJEhs1Apba3I+u5riX1dIkdptP5EKDG5XDPByWxtehw==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-named-capturing-groups-regex@7.24.7': + resolution: {integrity: sha512-/jr7h/EWeJtk1U/uz2jlsCioHkZk1JJZVcc8oQsJ1dUlaJD83f4/6Zeh2aHt9BIFokHIsSeDfhUmju0+1GPd6g==} + engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.2) - dev: false + '@babel/core': ^7.0.0 - /@babel/plugin-transform-for-of@7.22.15(@babel/core@7.23.2): - resolution: - { - integrity: sha512-me6VGeHsx30+xh9fbDLLPi0J1HzmeIIyenoOQHuw2D4m2SAU3NrspX5XxJLBpqn5yrLzrlw2Iy3RA//Bx27iOA==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-new-target@7.24.7': + resolution: {integrity: sha512-RNKwfRIXg4Ls/8mMTza5oPF5RkOW8Wy/WgMAp1/F1yZ8mMbtwXW+HDoJiOsagWrAhI5f57Vncrmr9XeT4CVapA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-transform-function-name@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-UIzQNMS0p0HHiQm3oelztj+ECwFnj+ZRV4KnguvlsD2of1whUeM6o7wGNj6oLwcDoAXQ8gEqfgC24D+VdIcevg==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-nullish-coalescing-operator@7.24.7': + resolution: {integrity: sha512-Ts7xQVk1OEocqzm8rHMXHlxvsfZ0cEF2yomUqpKENHWMF4zKk175Y4q8H5knJes6PgYad50uuRmt3UJuhBw8pQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-compilation-targets': 7.22.15 - '@babel/helper-function-name': 7.23.0 - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-transform-json-strings@7.22.11(@babel/core@7.23.2): - resolution: - { - integrity: sha512-CxT5tCqpA9/jXFlme9xIBCc5RPtdDq3JpkkhgHQqtDdiTnTI0jtZ0QzXhr5DILeYifDPp2wvY2ad+7+hLMW5Pw==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-numeric-separator@7.24.7': + resolution: {integrity: sha512-e6q1TiVUzvH9KRvicuxdBTUj4AdKSRwzIyFFnfnezpCfP2/7Qmbb8qbU2j7GODbl4JMkblitCQjKYUaX/qkkwA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.2) - dev: false - /@babel/plugin-transform-literals@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-fTLj4D79M+mepcw3dgFBTIDYpbcB9Sm0bpm4ppXPaO+U+PKFFyV9MGRvS0gvGw62sd10kT5lRMKXAADb9pWy8g==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-object-rest-spread@7.24.7': + resolution: {integrity: sha512-4QrHAr0aXQCEFni2q4DqKLD31n2DL+RxcwnNjDFkSG0eNQ/xCavnRkfCUjsyqGC2OviNJvZOF/mQqZBw7i2C5Q==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-transform-logical-assignment-operators@7.22.11(@babel/core@7.23.2): - resolution: - { - integrity: sha512-qQwRTP4+6xFCDV5k7gZBF3C31K34ut0tbEcTKxlX/0KXxm9GLcO14p570aWxFvVzx6QAfPgq7gaeIHXJC8LswQ==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-object-super@7.24.7': + resolution: {integrity: sha512-A/vVLwN6lBrMFmMDmPPz0jnE6ZGx7Jq7d6sT/Ev4H65RER6pZ+kczlf1DthF5N0qaPHBsI7UXiE8Zy66nmAovg==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.2) - dev: false - /@babel/plugin-transform-member-expression-literals@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-RZEdkNtzzYCFl9SE9ATaUMTj2hqMb4StarOJLrZRbqqU4HSBE7UlBw9WBWQiDzrJZJdUWiMTVDI6Gv/8DPvfew==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-optional-catch-binding@7.24.7': + resolution: {integrity: sha512-uLEndKqP5BfBbC/5jTwPxLh9kqPWWgzN/f8w6UwAIirAEqiIVJWWY312X72Eub09g5KF9+Zn7+hT7sDxmhRuKA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-transform-modules-amd@7.23.0(@babel/core@7.23.2): - resolution: - { - integrity: sha512-xWT5gefv2HGSm4QHtgc1sYPbseOyf+FFDo2JbpE25GWl5BqTGO9IMwTYJRoIdjsF85GE+VegHxSCUt5EvoYTAw==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-optional-chaining@7.24.7': + resolution: {integrity: sha512-tK+0N9yd4j+x/4hxF3F0e0fu/VdcxU18y5SevtyM/PCFlQvXbR0Zmlo2eBrKtVipGNFzpq56o8WsIIKcJFUCRQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.2) - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-transform-modules-commonjs@7.23.0(@babel/core@7.23.2): - resolution: - { - integrity: sha512-32Xzss14/UVc7k9g775yMIvkVK8xwKE0DPdP5JTapr3+Z9w4tzeOuLNY6BXDQR6BdnzIlXnCGAzsk/ICHBLVWQ==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-parameters@7.24.7': + resolution: {integrity: sha512-yGWW5Rr+sQOhK0Ot8hjDJuxU3XLRQGflvT4lhlSY0DFvdb3TwKaY26CJzHtYllU0vT9j58hc37ndFPsqT1SrzA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.2) - '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-simple-access': 7.22.5 - dev: false - /@babel/plugin-transform-modules-systemjs@7.23.0(@babel/core@7.23.2): - resolution: - { - integrity: sha512-qBej6ctXZD2f+DhlOC9yO47yEYgUh5CZNz/aBoH4j/3NOlRfJXJbY7xDQCqQVf9KbrqGzIWER1f23doHGrIHFg==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-private-methods@7.24.7': + resolution: {integrity: sha512-COTCOkG2hn4JKGEKBADkA8WNb35TGkkRbI5iT845dB+NyqgO8Hn+ajPbSnIQznneJTa3d30scb6iz/DhH8GsJQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-hoist-variables': 7.22.5 - '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.2) - '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-validator-identifier': 7.22.20 - dev: false - /@babel/plugin-transform-modules-umd@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-+S6kzefN/E1vkSsKx8kmQuqeQsvCKCd1fraCM7zXm4SFoggI099Tr4G8U81+5gtMdUeMQ4ipdQffbKLX0/7dBQ==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-private-property-in-object@7.24.7': + resolution: {integrity: sha512-9z76mxwnwFxMyxZWEgdgECQglF2Q7cFLm0kMf8pGwt+GSJsY0cONKj/UuO4bOH0w/uAel3ekS4ra5CEAyJRmDA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-module-transforms': 7.23.0(@babel/core@7.23.2) - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-property-literals@7.24.7': + resolution: {integrity: sha512-EMi4MLQSHfd2nrCqQEWxFdha2gBCqU4ZcCng4WBGZ5CJL4bBRW0ptdqqDdeirGZcpALazVVNJqRmsO8/+oNCBA==} + engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.2) - '@babel/helper-plugin-utils': 7.22.5 - dev: false + '@babel/core': ^7.0.0-0 - /@babel/plugin-transform-new-target@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-AsF7K0Fx/cNKVyk3a+DW0JLo+Ua598/NxMRvxDnkpCIGFh43+h/v2xyhRUYf6oD8gE4QtL83C7zZVghMjHd+iw==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-regenerator@7.24.7': + resolution: {integrity: sha512-lq3fvXPdimDrlg6LWBoqj+r/DEWgONuwjuOuQCSYgRroXDH/IdM1C0IZf59fL5cHLpjEH/O6opIRBbqv7ELnuA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-transform-nullish-coalescing-operator@7.22.11(@babel/core@7.23.2): - resolution: - { - integrity: sha512-YZWOw4HxXrotb5xsjMJUDlLgcDXSfO9eCmdl1bgW4+/lAGdkjaEvOnQ4p5WKKdUgSzO39dgPl0pTnfxm0OAXcg==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-reserved-words@7.24.7': + resolution: {integrity: sha512-0DUq0pHcPKbjFZCfTss/pGkYMfy3vFWydkUBd9r0GHpIyfs2eCDENvqadMycRS9wZCXR41wucAfJHJmwA0UmoQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.2) - dev: false - /@babel/plugin-transform-numeric-separator@7.22.11(@babel/core@7.23.2): - resolution: - { - integrity: sha512-3dzU4QGPsILdJbASKhF/V2TVP+gJya1PsueQCxIPCEcerqF21oEcrob4mzjsp2Py/1nLfF5m+xYNMDpmA8vffg==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-runtime@7.24.7': + resolution: {integrity: sha512-YqXjrk4C+a1kZjewqt+Mmu2UuV1s07y8kqcUf4qYLnoqemhR4gRQikhdAhSVJioMjVTu6Mo6pAbaypEA3jY6fw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.2) - dev: false - /@babel/plugin-transform-object-rest-spread@7.22.15(@babel/core@7.23.2): - resolution: - { - integrity: sha512-fEB+I1+gAmfAyxZcX1+ZUwLeAuuf8VIg67CTznZE0MqVFumWkh8xWtn58I4dxdVf080wn7gzWoF8vndOViJe9Q==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-shorthand-properties@7.24.7': + resolution: {integrity: sha512-KsDsevZMDsigzbA09+vacnLpmPH4aWjcZjXdyFKGzpplxhbeB4wYtury3vglQkg6KM/xEPKt73eCjPPf1PgXBA==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/compat-data': 7.23.2 - '@babel/core': 7.23.2 - '@babel/helper-compilation-targets': 7.22.15 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.2) - '@babel/plugin-transform-parameters': 7.22.15(@babel/core@7.23.2) - dev: false - - /@babel/plugin-transform-object-super@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-klXqyaT9trSjIUrcsYIfETAzmOEZL3cBYqOYLJxBHfMFFggmXOv+NYSX/Jbs9mzMVESw/WycLFPRx8ba/b2Ipw==, - } - engines: { node: '>=6.9.0' } + + '@babel/plugin-transform-spread@7.24.7': + resolution: {integrity: sha512-x96oO0I09dgMDxJaANcRyD4ellXFLLiWhuwDxKZX5g2rWP1bTPkBSwCYv96VDXVT1bD9aPj8tppr5ITIh8hBng==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.2) - dev: false - /@babel/plugin-transform-optional-catch-binding@7.22.11(@babel/core@7.23.2): - resolution: - { - integrity: sha512-rli0WxesXUeCJnMYhzAglEjLWVDF6ahb45HuprcmQuLidBJFWjNnOzssk2kuc6e33FlLaiZhG/kUIzUMWdBKaQ==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-sticky-regex@7.24.7': + resolution: {integrity: sha512-kHPSIJc9v24zEml5geKg9Mjx5ULpfncj0wRpYtxbvKyTtHCYDkVE3aHQ03FrpEo4gEe2vrJJS1Y9CJTaThA52g==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.2) - dev: false - /@babel/plugin-transform-optional-chaining@7.23.0(@babel/core@7.23.2): - resolution: - { - integrity: sha512-sBBGXbLJjxTzLBF5rFWaikMnOGOk/BmK6vVByIdEggZ7Vn6CvWXZyRkkLFK6WE0IF8jSliyOkUN6SScFgzCM0g==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-template-literals@7.24.7': + resolution: {integrity: sha512-AfDTQmClklHCOLxtGoP7HkeMw56k1/bTQjwsfhL6pppo/M4TOBSq+jjBUBLmV/4oeFg4GWMavIl44ZeCtmmZTw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.2) - dev: false - /@babel/plugin-transform-parameters@7.22.15(@babel/core@7.23.2): - resolution: - { - integrity: sha512-hjk7qKIqhyzhhUvRT683TYQOFa/4cQKwQy7ALvTpODswN40MljzNDa0YldevS6tGbxwaEKVn502JmY0dP7qEtQ==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-typeof-symbol@7.24.7': + resolution: {integrity: sha512-VtR8hDy7YLB7+Pet9IarXjg/zgCMSF+1mNS/EQEiEaUPoFXCVsHG64SIxcaaI2zJgRiv+YmgaQESUfWAdbjzgg==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-transform-private-methods@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-PPjh4gyrQnGe97JTalgRGMuU4icsZFnWkzicB/fUtzlKUqvsWBKEpPPfr5a2JiyirZkHxnAqkQMO5Z5B2kK3fA==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-unicode-escapes@7.24.7': + resolution: {integrity: sha512-U3ap1gm5+4edc2Q/P+9VrBNhGkfnf+8ZqppY71Bo/pzZmXhhLdqgaUl6cuB07O1+AQJtCLfaOmswiNbSQ9ivhw==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.2) - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-transform-private-property-in-object@7.22.11(@babel/core@7.23.2): - resolution: - { - integrity: sha512-sSCbqZDBKHetvjSwpyWzhuHkmW5RummxJBVbYLkGkaiTOWGxml7SXt0iWa03bzxFIx7wOj3g/ILRd0RcJKBeSQ==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-unicode-property-regex@7.24.7': + resolution: {integrity: sha512-uH2O4OV5M9FZYQrwc7NdVmMxQJOCCzFeYudlZSzUAHRFeOujQefa92E74TQDVskNHCzOXoigEuoyzHDhaEaK5w==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-annotate-as-pure': 7.22.5 - '@babel/helper-create-class-features-plugin': 7.22.15(@babel/core@7.23.2) - '@babel/helper-plugin-utils': 7.22.5 - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.2) - dev: false - - /@babel/plugin-transform-property-literals@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-TiOArgddK3mK/x1Qwf5hay2pxI6wCZnvQqrFSqbtg1GLl2JcNMitVH/YnqjP+M31pLUeTfzY1HAXFDnUBV30rQ==, - } - engines: { node: '>=6.9.0' } + + '@babel/plugin-transform-unicode-regex@7.24.7': + resolution: {integrity: sha512-hlQ96MBZSAXUq7ltkjtu3FJCCSMx/j629ns3hA3pXnBXjanNP0LHi+JpPeA81zaWgVK1VGH95Xuy7u0RyQ8kMg==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-transform-regenerator@7.22.10(@babel/core@7.23.2): - resolution: - { - integrity: sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw==, - } - engines: { node: '>=6.9.0' } + '@babel/plugin-transform-unicode-sets-regex@7.24.7': + resolution: {integrity: sha512-2G8aAvF4wy1w/AGZkemprdGMRg5o6zPNhbHVImRz3lss55TYCBd6xStN19rt8XJHq20sqV0JbyWjOWwQRwV/wg==} + engines: {node: '>=6.9.0'} peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - regenerator-transform: 0.15.2 - dev: false + '@babel/core': ^7.0.0 - /@babel/plugin-transform-reserved-words@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-DTtGKFRQUDm8svigJzZHzb/2xatPc6TzNvAIJ5GqOKDsGFYgAskjRulbR/vGsPKq3OPqtexnz327qYpP57RFyA==, - } - engines: { node: '>=6.9.0' } + '@babel/preset-env@7.24.7': + resolution: {integrity: sha512-1YZNsc+y6cTvWlDHidMBsQZrZfEFjRIo/BZCT906PMdzOyXtSLTgqGdrpcuTDCXyd11Am5uQULtDIcCfnTc8fQ==} + engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: false - /@babel/plugin-transform-runtime@7.23.2(@babel/core@7.23.2): - resolution: - { - integrity: sha512-XOntj6icgzMS58jPVtQpiuF6ZFWxQiJavISGx5KGjRj+3gqZr8+N6Kx+N9BApWzgS+DOjIZfXXj0ZesenOWDyA==, - } - engines: { node: '>=6.9.0' } + '@babel/preset-modules@0.1.6-no-external-plugins': + resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==} peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-module-imports': 7.22.15 - '@babel/helper-plugin-utils': 7.22.5 - babel-plugin-polyfill-corejs2: 0.4.6(@babel/core@7.23.2) - babel-plugin-polyfill-corejs3: 0.8.6(@babel/core@7.23.2) - babel-plugin-polyfill-regenerator: 0.5.3(@babel/core@7.23.2) - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - dev: false + '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 - /@babel/plugin-transform-shorthand-properties@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-vM4fq9IXHscXVKzDv5itkO1X52SmdFBFcMIBZ2FRn2nqVYqw6dBexUgMvAjHW+KXpPPViD/Yo3GrDEBaRC0QYA==, - } - engines: { node: '>=6.9.0' } - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: false + '@babel/regjsgen@0.8.0': + resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==} - /@babel/plugin-transform-spread@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-5ZzDQIGyvN4w8+dMmpohL6MBo+l2G7tfC/O2Dg7/hjpgeWvUx8FzfeOKxGog9IimPa4YekaQ9PlDqTLOljkcxg==, - } - engines: { node: '>=6.9.0' } - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 - dev: false + '@babel/runtime@7.24.5': + resolution: {integrity: sha512-Nms86NXrsaeU9vbBJKni6gXiEXZ4CVpYVzEjDH9Sb8vmZ3UljyA1GSOJl/6LGPO8EHLuSF9H+IxNXHPX8QHJ4g==} + engines: {node: '>=6.9.0'} - /@babel/plugin-transform-sticky-regex@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-zf7LuNpHG0iEeiyCNwX4j3gDg1jgt1k3ZdXBKbZSoA3BbGQGvMiSvfbZRR3Dr3aeJe3ooWFZxOOG3IRStYp2Bw==, - } - engines: { node: '>=6.9.0' } - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: false + '@babel/runtime@7.24.7': + resolution: {integrity: sha512-UwgBRMjJP+xv857DCngvqXI3Iq6J4v0wXmwc6sapg+zyhbwmQX67LUEFrkK5tbyJ30jGuG3ZvWpBiB9LCy1kWw==} + engines: {node: '>=6.9.0'} - /@babel/plugin-transform-template-literals@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-5ciOehRNf+EyUeewo8NkbQiUs4d6ZxiHo6BcBcnFlgiJfu16q0bQUw9Jvo0b0gBKFG1SMhDSjeKXSYuJLeFSMA==, - } - engines: { node: '>=6.9.0' } - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: false + '@babel/standalone@7.23.1': + resolution: {integrity: sha512-a4muOYz1qUaSoybuUKwK90mRG4sf5rBeUbuzpuGLzG32ZDE/Y2YEebHDODFJN+BtyOKi19hrLfq2qbNyKMx0TA==} + engines: {node: '>=6.9.0'} - /@babel/plugin-transform-typeof-symbol@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-bYkI5lMzL4kPii4HHEEChkD0rkc+nvnlR6+o/qdqR6zrm0Sv/nodmyLhlq2DO0YKLUNd2VePmPRjJXSBh9OIdA==, - } - engines: { node: '>=6.9.0' } - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: false + '@babel/standalone@7.24.7': + resolution: {integrity: sha512-QRIRMJ2KTeN+vt4l9OjYlxDVXEpcor1Z6V7OeYzeBOw6Q8ew9oMTHjzTx8s6ClsZO7wVf6JgTRutihatN6K0yA==} + engines: {node: '>=6.9.0'} - /@babel/plugin-transform-unicode-escapes@7.22.10(@babel/core@7.23.2): - resolution: - { - integrity: sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg==, - } - engines: { node: '>=6.9.0' } - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - dev: false + '@babel/template@7.24.7': + resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==} + engines: {node: '>=6.9.0'} - /@babel/plugin-transform-unicode-property-regex@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-HCCIb+CbJIAE6sXn5CjFQXMwkCClcOfPCzTlilJ8cUatfzwHlWQkbtV0zD338u9dZskwvuOYTuuaMaA8J5EI5A==, - } - engines: { node: '>=6.9.0' } - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.2) - '@babel/helper-plugin-utils': 7.22.5 - dev: false + '@babel/template@7.27.2': + resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} + engines: {node: '>=6.9.0'} - /@babel/plugin-transform-unicode-regex@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-028laaOKptN5vHJf9/Arr/HiJekMd41hOEZYvNsrsXqJ7YPYuX2bQxh31fkZzGmq3YqHRJzYFFAVYvKfMPKqyg==, - } - engines: { node: '>=6.9.0' } - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.2) - '@babel/helper-plugin-utils': 7.22.5 - dev: false + '@babel/traverse@7.24.7': + resolution: {integrity: sha512-yb65Ed5S/QAcewNPh0nZczy9JdYXkkAbIsEo+P7BE7yO3txAY30Y/oPa3QkQ5It3xVG2kpKMg9MsdxZaO31uKA==} + engines: {node: '>=6.9.0'} - /@babel/plugin-transform-unicode-sets-regex@7.22.5(@babel/core@7.23.2): - resolution: - { - integrity: sha512-lhMfi4FC15j13eKrh3DnYHjpGj6UKQHtNKTbtc1igvAhRy4+kLhV07OpLcsN0VgDEw/MjAvJO4BdMJsHwMhzCg==, - } - engines: { node: '>=6.9.0' } - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.2) - '@babel/helper-plugin-utils': 7.22.5 - dev: false + '@babel/traverse@7.28.4': + resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==} + engines: {node: '>=6.9.0'} - /@babel/preset-env@7.23.2(@babel/core@7.23.2): - resolution: - { - integrity: sha512-BW3gsuDD+rvHL2VO2SjAUNTBe5YrjsTiDyqamPDWY723na3/yPQ65X5oQkFVJZ0o50/2d+svm1rkPoJeR1KxVQ==, - } - engines: { node: '>=6.9.0' } - peerDependencies: - '@babel/core': ^7.0.0-0 - dependencies: - '@babel/compat-data': 7.23.2 - '@babel/core': 7.23.2 - '@babel/helper-compilation-targets': 7.22.15 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/helper-validator-option': 7.22.15 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.22.15(@babel/core@7.23.2) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.22.15(@babel/core@7.23.2) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.2) - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.2) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.2) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.2) - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.2) - '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.2) - '@babel/plugin-syntax-import-assertions': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-syntax-import-attributes': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.2) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.2) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.2) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.2) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.2) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.2) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.2) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.2) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.2) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.2) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.23.2) - '@babel/plugin-transform-arrow-functions': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-async-generator-functions': 7.23.2(@babel/core@7.23.2) - '@babel/plugin-transform-async-to-generator': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-block-scoped-functions': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-block-scoping': 7.23.0(@babel/core@7.23.2) - '@babel/plugin-transform-class-properties': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-class-static-block': 7.22.11(@babel/core@7.23.2) - '@babel/plugin-transform-classes': 7.22.15(@babel/core@7.23.2) - '@babel/plugin-transform-computed-properties': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-destructuring': 7.23.0(@babel/core@7.23.2) - '@babel/plugin-transform-dotall-regex': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-duplicate-keys': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-dynamic-import': 7.22.11(@babel/core@7.23.2) - '@babel/plugin-transform-exponentiation-operator': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-export-namespace-from': 7.22.11(@babel/core@7.23.2) - '@babel/plugin-transform-for-of': 7.22.15(@babel/core@7.23.2) - '@babel/plugin-transform-function-name': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-json-strings': 7.22.11(@babel/core@7.23.2) - '@babel/plugin-transform-literals': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-logical-assignment-operators': 7.22.11(@babel/core@7.23.2) - '@babel/plugin-transform-member-expression-literals': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-modules-amd': 7.23.0(@babel/core@7.23.2) - '@babel/plugin-transform-modules-commonjs': 7.23.0(@babel/core@7.23.2) - '@babel/plugin-transform-modules-systemjs': 7.23.0(@babel/core@7.23.2) - '@babel/plugin-transform-modules-umd': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-named-capturing-groups-regex': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-new-target': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-nullish-coalescing-operator': 7.22.11(@babel/core@7.23.2) - '@babel/plugin-transform-numeric-separator': 7.22.11(@babel/core@7.23.2) - '@babel/plugin-transform-object-rest-spread': 7.22.15(@babel/core@7.23.2) - '@babel/plugin-transform-object-super': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-optional-catch-binding': 7.22.11(@babel/core@7.23.2) - '@babel/plugin-transform-optional-chaining': 7.23.0(@babel/core@7.23.2) - '@babel/plugin-transform-parameters': 7.22.15(@babel/core@7.23.2) - '@babel/plugin-transform-private-methods': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-private-property-in-object': 7.22.11(@babel/core@7.23.2) - '@babel/plugin-transform-property-literals': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-regenerator': 7.22.10(@babel/core@7.23.2) - '@babel/plugin-transform-reserved-words': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-shorthand-properties': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-spread': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-sticky-regex': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-template-literals': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-typeof-symbol': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-unicode-escapes': 7.22.10(@babel/core@7.23.2) - '@babel/plugin-transform-unicode-property-regex': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-unicode-regex': 7.22.5(@babel/core@7.23.2) - '@babel/plugin-transform-unicode-sets-regex': 7.22.5(@babel/core@7.23.2) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.23.2) - '@babel/types': 7.23.0 - babel-plugin-polyfill-corejs2: 0.4.6(@babel/core@7.23.2) - babel-plugin-polyfill-corejs3: 0.8.6(@babel/core@7.23.2) - babel-plugin-polyfill-regenerator: 0.5.3(@babel/core@7.23.2) - core-js-compat: 3.33.2 - semver: 6.3.1 - transitivePeerDependencies: - - supports-color - dev: false + '@babel/types@7.28.2': + resolution: {integrity: sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==} + engines: {node: '>=6.9.0'} - /@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.23.2): - resolution: - { - integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==, - } - peerDependencies: - '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0 - dependencies: - '@babel/core': 7.23.2 - '@babel/helper-plugin-utils': 7.22.5 - '@babel/types': 7.23.0 - esutils: 2.0.3 - dev: false - - /@babel/regjsgen@0.8.0: - resolution: - { - integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==, - } - dev: false - - /@babel/runtime@7.23.1: - resolution: - { - integrity: sha512-hC2v6p8ZSI/W0HUzh3V8C5g+NwSKzKPtJwSpTjwl0o297GP9+ZLQSkdvHz46CM3LqyoXxq+5G9komY+eSqSO0g==, - } - engines: { node: '>=6.9.0' } - dependencies: - regenerator-runtime: 0.14.0 - dev: false - - /@babel/runtime@7.23.2: - resolution: - { - integrity: sha512-mM8eg4yl5D6i3lu2QKPuPH4FArvJ8KhTofbE7jwMUv9KX5mBvwPAqnV3MlyBNqdp9RyRKP6Yck8TrfYrPvX3bg==, - } - engines: { node: '>=6.9.0' } - dependencies: - regenerator-runtime: 0.14.0 - dev: false - - /@babel/standalone@7.23.1: - resolution: - { - integrity: sha512-a4muOYz1qUaSoybuUKwK90mRG4sf5rBeUbuzpuGLzG32ZDE/Y2YEebHDODFJN+BtyOKi19hrLfq2qbNyKMx0TA==, - } - engines: { node: '>=6.9.0' } - dev: true - - /@babel/template@7.22.15: - resolution: - { - integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==, - } - engines: { node: '>=6.9.0' } - dependencies: - '@babel/code-frame': 7.22.13 - '@babel/parser': 7.23.0 - '@babel/types': 7.23.0 + '@babel/types@7.28.4': + resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==} + engines: {node: '>=6.9.0'} - /@babel/traverse@7.23.2: - resolution: - { - integrity: sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==, - } - engines: { node: '>=6.9.0' } - dependencies: - '@babel/code-frame': 7.22.13 - '@babel/generator': 7.23.0 - '@babel/helper-environment-visitor': 7.22.20 - '@babel/helper-function-name': 7.23.0 - '@babel/helper-hoist-variables': 7.22.5 - '@babel/helper-split-export-declaration': 7.22.6 - '@babel/parser': 7.23.0 - '@babel/types': 7.23.0 - debug: 4.3.4 - globals: 11.12.0 - transitivePeerDependencies: - - supports-color + '@bcoe/v8-coverage@0.2.3': + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} - /@babel/types@7.23.0: - resolution: - { - integrity: sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==, - } - engines: { node: '>=6.9.0' } - dependencies: - '@babel/helper-string-parser': 7.22.5 - '@babel/helper-validator-identifier': 7.22.20 - to-fast-properties: 2.0.0 - - /@bcoe/v8-coverage@0.2.3: - resolution: - { - integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==, - } - dev: true - - /@commitlint/cli@17.7.2: - resolution: - { - integrity: sha512-t3N7TZq7lOeqTOyEgfGcaltHqEJf7YDlPg75MldeVPPyz14jZq/+mbGF9tueDLFX8R6RwdymrN6D+U5XwZ8Iwg==, - } - engines: { node: '>=v14' } + '@commitlint/cli@20.4.0': + resolution: {integrity: sha512-2lqrFrYNxjKxgMqeYiO3zNM14XN9v72/5xIJyvdLw7sHEGlfg6sweW01PGNWiqZa6/AuZwsb0uzkgWJy6F4N2w==} + engines: {node: '>=v18'} hasBin: true - dependencies: - '@commitlint/format': 17.4.4 - '@commitlint/lint': 17.7.0 - '@commitlint/load': 17.7.2 - '@commitlint/read': 17.5.1 - '@commitlint/types': 17.4.4 - execa: 5.1.1 - lodash.isfunction: 3.0.9 - resolve-from: 5.0.0 - resolve-global: 1.0.0 - yargs: 17.7.2 - transitivePeerDependencies: - - '@swc/core' - - '@swc/wasm' - dev: true - - /@commitlint/config-conventional@18.1.0: - resolution: - { - integrity: sha512-8vvvtV3GOLEMHeKc8PjRL1lfP1Y4B6BG0WroFd9PJeRiOc3nFX1J0wlJenLURzl9Qus6YXVGWf+a/ZlbCKT3AA==, - } - engines: { node: '>=v18' } - dependencies: - conventional-changelog-conventionalcommits: 7.0.2 - dev: true - - /@commitlint/config-validator@17.6.7: - resolution: - { - integrity: sha512-vJSncmnzwMvpr3lIcm0I8YVVDJTzyjy7NZAeXbTXy+MPUdAr9pKyyg7Tx/ebOQ9kqzE6O9WT6jg2164br5UdsQ==, - } - engines: { node: '>=v14' } - dependencies: - '@commitlint/types': 17.4.4 - ajv: 8.12.0 - dev: true - /@commitlint/ensure@17.6.7: - resolution: - { - integrity: sha512-mfDJOd1/O/eIb/h4qwXzUxkmskXDL9vNPnZ4AKYKiZALz4vHzwMxBSYtyL2mUIDeU9DRSpEUins8SeKtFkYHSw==, - } - engines: { node: '>=v14' } - dependencies: - '@commitlint/types': 17.4.4 - lodash.camelcase: 4.3.0 - lodash.kebabcase: 4.1.1 - lodash.snakecase: 4.1.1 - lodash.startcase: 4.4.0 - lodash.upperfirst: 4.3.1 - dev: true - - /@commitlint/execute-rule@17.4.0: - resolution: - { - integrity: sha512-LIgYXuCSO5Gvtc0t9bebAMSwd68ewzmqLypqI2Kke1rqOqqDbMpYcYfoPfFlv9eyLIh4jocHWwCK5FS7z9icUA==, - } - engines: { node: '>=v14' } - dev: true - - /@commitlint/format@17.4.4: - resolution: - { - integrity: sha512-+IS7vpC4Gd/x+uyQPTAt3hXs5NxnkqAZ3aqrHd5Bx/R9skyCAWusNlNbw3InDbAK6j166D9asQM8fnmYIa+CXQ==, - } - engines: { node: '>=v14' } - dependencies: - '@commitlint/types': 17.4.4 - chalk: 4.1.2 - dev: true + '@commitlint/config-conventional@20.4.0': + resolution: {integrity: sha512-nolhFe2YKIix0D4+tPXAWnnIc9WB5fOCgmm4h2EcRyEShC64oH/DpM9n++85NRdItvIhKb+Szsaeuug7KcEeIA==} + engines: {node: '>=v18'} - /@commitlint/is-ignored@17.7.0: - resolution: - { - integrity: sha512-043rA7m45tyEfW7Zv2vZHF++176MLHH9h70fnPoYlB1slKBeKl8BwNIlnPg4xBdRBVNPaCqvXxWswx2GR4c9Hw==, - } - engines: { node: '>=v14' } - dependencies: - '@commitlint/types': 17.4.4 - semver: 7.5.4 - dev: true - - /@commitlint/lint@17.7.0: - resolution: - { - integrity: sha512-TCQihm7/uszA5z1Ux1vw+Nf3yHTgicus/+9HiUQk+kRSQawByxZNESeQoX9ujfVd3r4Sa+3fn0JQAguG4xvvbA==, - } - engines: { node: '>=v14' } - dependencies: - '@commitlint/is-ignored': 17.7.0 - '@commitlint/parse': 17.7.0 - '@commitlint/rules': 17.7.0 - '@commitlint/types': 17.4.4 - dev: true - - /@commitlint/load@17.7.2: - resolution: - { - integrity: sha512-XA7WTnsjHZ4YH6ZYsrnxgLdXzriwMMq+utZUET6spbOEEIPBCDLdOQXS26P+v3TTO4hUHOEhzUquaBv3jbBixw==, - } - engines: { node: '>=v14' } - dependencies: - '@commitlint/config-validator': 17.6.7 - '@commitlint/execute-rule': 17.4.0 - '@commitlint/resolve-extends': 17.6.7 - '@commitlint/types': 17.4.4 - '@types/node': 20.5.1 - chalk: 4.1.2 - cosmiconfig: 8.3.6(typescript@4.9.5) - cosmiconfig-typescript-loader: 4.4.0(@types/node@20.5.1)(cosmiconfig@8.3.6)(ts-node@10.9.1)(typescript@5.2.2) - lodash.isplainobject: 4.0.6 - lodash.merge: 4.6.2 - lodash.uniq: 4.5.0 - resolve-from: 5.0.0 - ts-node: 10.9.1(@types/node@20.5.1)(typescript@4.9.5) - typescript: 5.2.2 - transitivePeerDependencies: - - '@swc/core' - - '@swc/wasm' - dev: true - - /@commitlint/message@17.4.2: - resolution: - { - integrity: sha512-3XMNbzB+3bhKA1hSAWPCQA3lNxR4zaeQAQcHj0Hx5sVdO6ryXtgUBGGv+1ZCLMgAPRixuc6en+iNAzZ4NzAa8Q==, - } - engines: { node: '>=v14' } - dev: true - - /@commitlint/parse@17.7.0: - resolution: - { - integrity: sha512-dIvFNUMCUHqq5Abv80mIEjLVfw8QNuA4DS7OWip4pcK/3h5wggmjVnlwGCDvDChkw2TjK1K6O+tAEV78oxjxag==, - } - engines: { node: '>=v14' } - dependencies: - '@commitlint/types': 17.4.4 - conventional-changelog-angular: 6.0.0 - conventional-commits-parser: 4.0.0 - dev: true - - /@commitlint/read@17.5.1: - resolution: - { - integrity: sha512-7IhfvEvB//p9aYW09YVclHbdf1u7g7QhxeYW9ZHSO8Huzp8Rz7m05aCO1mFG7G8M+7yfFnXB5xOmG18brqQIBg==, - } - engines: { node: '>=v14' } - dependencies: - '@commitlint/top-level': 17.4.0 - '@commitlint/types': 17.4.4 - fs-extra: 11.1.1 - git-raw-commits: 2.0.11 - minimist: 1.2.8 - dev: true + '@commitlint/config-validator@20.4.0': + resolution: {integrity: sha512-zShmKTF+sqyNOfAE0vKcqnpvVpG0YX8F9G/ZIQHI2CoKyK+PSdladXMSns400aZ5/QZs+0fN75B//3Q5CHw++w==} + engines: {node: '>=v18'} - /@commitlint/resolve-extends@17.6.7: - resolution: - { - integrity: sha512-PfeoAwLHtbOaC9bGn/FADN156CqkFz6ZKiVDMjuC2N5N0740Ke56rKU7Wxdwya8R8xzLK9vZzHgNbuGhaOVKIg==, - } - engines: { node: '>=v14' } - dependencies: - '@commitlint/config-validator': 17.6.7 - '@commitlint/types': 17.4.4 - import-fresh: 3.3.0 - lodash.mergewith: 4.6.2 - resolve-from: 5.0.0 - resolve-global: 1.0.0 - dev: true - - /@commitlint/rules@17.7.0: - resolution: - { - integrity: sha512-J3qTh0+ilUE5folSaoK91ByOb8XeQjiGcdIdiB/8UT1/Rd1itKo0ju/eQVGyFzgTMYt8HrDJnGTmNWwcMR1rmA==, - } - engines: { node: '>=v14' } - dependencies: - '@commitlint/ensure': 17.6.7 - '@commitlint/message': 17.4.2 - '@commitlint/to-lines': 17.4.0 - '@commitlint/types': 17.4.4 - execa: 5.1.1 - dev: true - - /@commitlint/to-lines@17.4.0: - resolution: - { - integrity: sha512-LcIy/6ZZolsfwDUWfN1mJ+co09soSuNASfKEU5sCmgFCvX5iHwRYLiIuoqXzOVDYOy7E7IcHilr/KS0e5T+0Hg==, - } - engines: { node: '>=v14' } - dev: true - - /@commitlint/top-level@17.4.0: - resolution: - { - integrity: sha512-/1loE/g+dTTQgHnjoCy0AexKAEFyHsR2zRB4NWrZ6lZSMIxAhBJnmCqwao7b4H8888PsfoTBCLBYIw8vGnej8g==, - } - engines: { node: '>=v14' } - dependencies: - find-up: 5.0.0 - dev: true + '@commitlint/ensure@20.4.0': + resolution: {integrity: sha512-F3qwnanJUisFWwh44GYYmMOxfgJL1FKV73FCB5zxo8pw1CHkxXadGfDfzNkN8B3iqgSGusDN2+oDH6upBmLszA==} + engines: {node: '>=v18'} - /@commitlint/types@17.4.4: - resolution: - { - integrity: sha512-amRN8tRLYOsxRr6mTnGGGvB5EmW/4DDjLMgiwK3CCVEmN6Sr/6xePGEpWaspKkckILuUORCwe6VfDBw6uj4axQ==, - } - engines: { node: '>=v14' } - dependencies: - chalk: 4.1.2 - dev: true + '@commitlint/execute-rule@20.0.0': + resolution: {integrity: sha512-xyCoOShoPuPL44gVa+5EdZsBVao/pNzpQhkzq3RdtlFdKZtjWcLlUFQHSWBuhk5utKYykeJPSz2i8ABHQA+ZZw==} + engines: {node: '>=v18'} - /@cspotcode/source-map-support@0.8.1: - resolution: - { - integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==, - } - engines: { node: '>=12' } - dependencies: - '@jridgewell/trace-mapping': 0.3.9 - dev: true + '@commitlint/format@20.4.0': + resolution: {integrity: sha512-i3ki3WR0rgolFVX6r64poBHXM1t8qlFel1G1eCBvVgntE3fCJitmzSvH5JD/KVJN/snz6TfaX2CLdON7+s4WVQ==} + engines: {node: '>=v18'} + + '@commitlint/is-ignored@20.4.0': + resolution: {integrity: sha512-E8AHpedEfuf+lZatFvFiJXA4TtZgBZ10+A7HzFudaEmTPPE5o6MGswxbxUIGAciaHAFj/oTTmyFc6A5tcvxE3Q==} + engines: {node: '>=v18'} + + '@commitlint/lint@20.4.0': + resolution: {integrity: sha512-W90YCbm5h3Yg+btF5/X+cxsY6vd/H3tsFt6U7WBmDQSkKV8NmitYg89zeoSQyYEiQCwAsH0dcA+99aQtLZiSnw==} + engines: {node: '>=v18'} + + '@commitlint/load@20.4.0': + resolution: {integrity: sha512-Dauup/GfjwffBXRJUdlX/YRKfSVXsXZLnINXKz0VZkXdKDcaEILAi9oflHGbfydonJnJAbXEbF3nXPm9rm3G6A==} + engines: {node: '>=v18'} + + '@commitlint/message@20.4.0': + resolution: {integrity: sha512-B5lGtvHgiLAIsK5nLINzVW0bN5hXv+EW35sKhYHE8F7V9Uz1fR4tx3wt7mobA5UNhZKUNgB/+ldVMQE6IHZRyA==} + engines: {node: '>=v18'} + + '@commitlint/parse@20.4.0': + resolution: {integrity: sha512-NcRkqo/QUnuc1RgxRCIKTqobKzF0BKJ8h3i1jRyeZ+SEy5rO9dPNOh4BqrFsSznb5mnwETYB7ph9tUcthNkwAQ==} + engines: {node: '>=v18'} - /@csstools/cascade-layer-name-parser@1.0.5(@csstools/css-parser-algorithms@2.3.2)(@csstools/css-tokenizer@2.2.1): - resolution: - { - integrity: sha512-v/5ODKNBMfBl0us/WQjlfsvSlYxfZLhNMVIsuCPib2ulTwGKYbKJbwqw671+qH9Y4wvWVnu7LBChvml/wBKjFg==, - } - engines: { node: ^14 || ^16 || >=18 } + '@commitlint/read@20.4.0': + resolution: {integrity: sha512-QfpFn6/I240ySEGv7YWqho4vxqtPpx40FS7kZZDjUJ+eHxu3azfhy7fFb5XzfTqVNp1hNoI3tEmiEPbDB44+cg==} + engines: {node: '>=v18'} + + '@commitlint/resolve-extends@20.4.0': + resolution: {integrity: sha512-ay1KM8q0t+/OnlpqXJ+7gEFQNlUtSU5Gxr8GEwnVf2TPN3+ywc5DzL3JCxmpucqxfHBTFwfRMXxPRRnR5Ki20g==} + engines: {node: '>=v18'} + + '@commitlint/rules@20.4.0': + resolution: {integrity: sha512-E+UoAA7WA4xrre9lDyX2vL4Df26I+vqMN4D8JoW/L2xE/VRDvn533/ibhgSlGYDltB9nm2S+1lti3PagEwO0ag==} + engines: {node: '>=v18'} + + '@commitlint/to-lines@20.0.0': + resolution: {integrity: sha512-2l9gmwiCRqZNWgV+pX1X7z4yP0b3ex/86UmUFgoRt672Ez6cAM2lOQeHFRUTuE6sPpi8XBCGnd8Kh3bMoyHwJw==} + engines: {node: '>=v18'} + + '@commitlint/top-level@20.4.0': + resolution: {integrity: sha512-NDzq8Q6jmFaIIBC/GG6n1OQEaHdmaAAYdrZRlMgW6glYWGZ+IeuXmiymDvQNXPc82mVxq2KiE3RVpcs+1OeDeA==} + engines: {node: '>=v18'} + + '@commitlint/types@20.4.0': + resolution: {integrity: sha512-aO5l99BQJ0X34ft8b0h7QFkQlqxC6e7ZPVmBKz13xM9O8obDaM1Cld4sQlJDXXU/VFuUzQ30mVtHjVz74TuStw==} + engines: {node: '>=v18'} + + '@csstools/cascade-layer-name-parser@1.0.12': + resolution: {integrity: sha512-iNCCOnaoycAfcIot3v/orjkTol+j8+Z5xgpqxUpZSdqeaxCADQZtldHhlvzDipmi7OoWdcJUO6DRZcnkMSBEIg==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: - '@csstools/css-parser-algorithms': ^2.3.2 - '@csstools/css-tokenizer': ^2.2.1 - dependencies: - '@csstools/css-parser-algorithms': 2.3.2(@csstools/css-tokenizer@2.2.1) - '@csstools/css-tokenizer': 2.2.1 - dev: false - - /@csstools/color-helpers@3.0.2: - resolution: - { - integrity: sha512-NMVs/l7Y9eIKL5XjbCHEgGcG8LOUT2qVcRjX6EzkCdlvftHVKr2tHIPzHavfrULRZ5Q2gxrJ9f44dAlj6fX97Q==, - } - engines: { node: ^14 || ^16 || >=18 } - dev: false - - /@csstools/css-calc@1.1.4(@csstools/css-parser-algorithms@2.3.2)(@csstools/css-tokenizer@2.2.1): - resolution: - { - integrity: sha512-ZV1TSmToiNcQL1P3hfzlzZzA02mmVkVmXGaUDUqpYUG84PmLhVSZpKX+KfxAuOcK7de04UXSQPBrAvaya6iiGg==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/css-parser-algorithms': ^2.7.0 + '@csstools/css-tokenizer': ^2.3.2 + + '@csstools/color-helpers@4.2.1': + resolution: {integrity: sha512-CEypeeykO9AN7JWkr1OEOQb0HRzZlPWGwV0Ya6DuVgFdDi6g3ma/cPZ5ZPZM4AWQikDpq/0llnGGlIL+j8afzw==} + engines: {node: ^14 || ^16 || >=18} + + '@csstools/color-helpers@5.1.0': + resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==} + engines: {node: '>=18'} + + '@csstools/css-calc@1.2.3': + resolution: {integrity: sha512-rlOh81K3CvtY969Od5b1h29YT6MpCHejMCURKrRrXFeCpz67HGaBNvBmWT5S7S+CKn+V7KJ+qxSmK8jNd/aZWA==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: - '@csstools/css-parser-algorithms': ^2.3.2 - '@csstools/css-tokenizer': ^2.2.1 - dependencies: - '@csstools/css-parser-algorithms': 2.3.2(@csstools/css-tokenizer@2.2.1) - '@csstools/css-tokenizer': 2.2.1 - dev: false + '@csstools/css-parser-algorithms': ^2.7.0 + '@csstools/css-tokenizer': ^2.3.2 - /@csstools/css-color-parser@1.4.0(@csstools/css-parser-algorithms@2.3.2)(@csstools/css-tokenizer@2.2.1): - resolution: - { - integrity: sha512-SlGd8E6ron24JYQPQAIzu5tvmWi1H4sDKTdA7UDnwF45oJv7AVESbOlOO1YjfBhrQFuvLWUgKiOY9DwGoAxwTA==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/css-calc@1.2.4': + resolution: {integrity: sha512-tfOuvUQeo7Hz+FcuOd3LfXVp+342pnWUJ7D2y8NUpu1Ww6xnTbHLpz018/y6rtbHifJ3iIEf9ttxXd8KG7nL0Q==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: - '@csstools/css-parser-algorithms': ^2.3.2 - '@csstools/css-tokenizer': ^2.2.1 - dependencies: - '@csstools/color-helpers': 3.0.2 - '@csstools/css-calc': 1.1.4(@csstools/css-parser-algorithms@2.3.2)(@csstools/css-tokenizer@2.2.1) - '@csstools/css-parser-algorithms': 2.3.2(@csstools/css-tokenizer@2.2.1) - '@csstools/css-tokenizer': 2.2.1 - dev: false + '@csstools/css-parser-algorithms': ^2.7.1 + '@csstools/css-tokenizer': ^2.4.1 - /@csstools/css-parser-algorithms@2.3.2(@csstools/css-tokenizer@2.2.1): - resolution: - { - integrity: sha512-sLYGdAdEY2x7TSw9FtmdaTrh2wFtRJO5VMbBrA8tEqEod7GEggFmxTSK9XqExib3yMuYNcvcTdCZIP6ukdjAIA==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/css-calc@2.1.4': + resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-color-parser@2.0.3': + resolution: {integrity: sha512-Qqhb5I/gEh1wI4brf6Kmy0Xn4J1IqO8OTDKWGRsBYtL4bGkHcV9i0XI2Mmo/UYFtSRoXW/RmKTcMh6sCI433Cw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + '@csstools/css-parser-algorithms': ^2.7.0 + '@csstools/css-tokenizer': ^2.3.2 + + '@csstools/css-color-parser@3.1.0': + resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-parser-algorithms': ^3.0.5 + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-parser-algorithms@2.3.2': + resolution: {integrity: sha512-sLYGdAdEY2x7TSw9FtmdaTrh2wFtRJO5VMbBrA8tEqEod7GEggFmxTSK9XqExib3yMuYNcvcTdCZIP6ukdjAIA==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: '@csstools/css-tokenizer': ^2.2.1 - dependencies: - '@csstools/css-tokenizer': 2.2.1 - /@csstools/css-tokenizer@2.2.1: - resolution: - { - integrity: sha512-Zmsf2f/CaEPWEVgw29odOj+WEVoiJy9s9NOv5GgNY9mZ1CZ7394By6wONrONrTsnNDv6F9hR02nvFihrGVGHBg==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/css-parser-algorithms@2.7.0': + resolution: {integrity: sha512-qvBMcOU/uWFCH/VO0MYe0AMs0BGMWAt6FTryMbFIKYtZtVnqTZtT8ktv5o718llkaGZWomJezJZjq3vJDHeJNQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + '@csstools/css-tokenizer': ^2.3.2 + + '@csstools/css-parser-algorithms@3.0.5': + resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==} + engines: {node: '>=18'} + peerDependencies: + '@csstools/css-tokenizer': ^3.0.4 + + '@csstools/css-tokenizer@2.2.1': + resolution: {integrity: sha512-Zmsf2f/CaEPWEVgw29odOj+WEVoiJy9s9NOv5GgNY9mZ1CZ7394By6wONrONrTsnNDv6F9hR02nvFihrGVGHBg==} + engines: {node: ^14 || ^16 || >=18} + + '@csstools/css-tokenizer@2.3.2': + resolution: {integrity: sha512-0xYOf4pQpAaE6Sm2Q0x3p25oRukzWQ/O8hWVvhIt9Iv98/uu053u2CGm/g3kJ+P0vOYTAYzoU8Evq2pg9ZPXtw==} + engines: {node: ^14 || ^16 || >=18} + + '@csstools/css-tokenizer@3.0.4': + resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} + engines: {node: '>=18'} + + '@csstools/media-query-list-parser@2.1.12': + resolution: {integrity: sha512-t1/CdyVJzOQUiGUcIBXRzTAkWTFPxiPnoKwowKW2z9Uj78c2bBWI/X94BeVfUwVq1xtCjD7dnO8kS6WONgp8Jw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + '@csstools/css-parser-algorithms': ^2.7.0 + '@csstools/css-tokenizer': ^2.3.2 - /@csstools/media-query-list-parser@2.1.5(@csstools/css-parser-algorithms@2.3.2)(@csstools/css-tokenizer@2.2.1): - resolution: - { - integrity: sha512-IxVBdYzR8pYe89JiyXQuYk4aVVoCPhMJkz6ElRwlVysjwURTsTk/bmY/z4FfeRE+CRBMlykPwXEVUg8lThv7AQ==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/media-query-list-parser@2.1.5': + resolution: {integrity: sha512-IxVBdYzR8pYe89JiyXQuYk4aVVoCPhMJkz6ElRwlVysjwURTsTk/bmY/z4FfeRE+CRBMlykPwXEVUg8lThv7AQ==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: '@csstools/css-parser-algorithms': ^2.3.2 '@csstools/css-tokenizer': ^2.2.1 - dependencies: - '@csstools/css-parser-algorithms': 2.3.2(@csstools/css-tokenizer@2.2.1) - '@csstools/css-tokenizer': 2.2.1 - /@csstools/postcss-cascade-layers@4.0.1(postcss@8.4.31): - resolution: - { - integrity: sha512-UYFuFL9GgVnftg9v7tBvVEBRLaBeAD66euD+yYy5fYCUld9ZIWTJNCE30hm6STMEdt6FL5xzeVw1lAZ1tpvUEg==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-cascade-layers@4.0.6': + resolution: {integrity: sha512-Xt00qGAQyqAODFiFEJNkTpSUz5VfYqnDLECdlA/Vv17nl/OIV5QfTRHGAXrBGG5YcJyHpJ+GF9gF/RZvOQz4oA==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - '@csstools/selector-specificity': 3.0.0(postcss-selector-parser@6.0.13) - postcss: 8.4.31 - postcss-selector-parser: 6.0.13 - dev: false - /@csstools/postcss-color-function@3.0.7(postcss@8.4.31): - resolution: - { - integrity: sha512-/PIB20G1TPCXmQlaJLWIYzTZRZpj6csT4ijgnshIj/kcmniIRroAfDa0xSWnfuO1eNo0NptIaPU7jzUukWn55Q==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-color-function@3.0.17': + resolution: {integrity: sha512-hi6g5KHMvxpxf01LCVu5xnNxX5h2Vkn9aKRmspn2esWjWtshuTXVOavTjwvogA+Eycm9Rn21QTYNU+qbKw6IeQ==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - '@csstools/css-color-parser': 1.4.0(@csstools/css-parser-algorithms@2.3.2)(@csstools/css-tokenizer@2.2.1) - '@csstools/css-parser-algorithms': 2.3.2(@csstools/css-tokenizer@2.2.1) - '@csstools/css-tokenizer': 2.2.1 - '@csstools/postcss-progressive-custom-properties': 3.0.2(postcss@8.4.31) - postcss: 8.4.31 - dev: false - /@csstools/postcss-color-mix-function@2.0.7(postcss@8.4.31): - resolution: - { - integrity: sha512-57/g8aGo5eKFjEeJMiRKh8Qq43K2rCyk5ZZTvJ34TNl4zUtYU5DvLkIkOnhCtL8/a4z9oMA42aOnFPddRrScUQ==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-color-mix-function@2.0.17': + resolution: {integrity: sha512-Y65GHGCY1R+9+/5KrJjN7gAF1NZydng4AGknMggeUJIyo2ckLb4vBrlDmpIcHDdjQtV5631j1hxvalVTbpoiFw==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - '@csstools/css-color-parser': 1.4.0(@csstools/css-parser-algorithms@2.3.2)(@csstools/css-tokenizer@2.2.1) - '@csstools/css-parser-algorithms': 2.3.2(@csstools/css-tokenizer@2.2.1) - '@csstools/css-tokenizer': 2.2.1 - '@csstools/postcss-progressive-custom-properties': 3.0.2(postcss@8.4.31) - postcss: 8.4.31 - dev: false - /@csstools/postcss-exponential-functions@1.0.1(postcss@8.4.31): - resolution: - { - integrity: sha512-ZLK2iSK4DUxeypGce2PnQSdYugUqDTwxnhNiq1o6OyKMNYgYs4eKbvEhFG8JKr1sJWbeqBi5jRr0017l2EWVvg==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-exponential-functions@1.0.8': + resolution: {integrity: sha512-/4WHpu4MrCCsUWRaDreyBcdF+5xnudk1JJLg6aWREeMaSpr3vsD0eywmOXct3xUm28TCqKS//S86IlcDJJdzoQ==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - '@csstools/css-calc': 1.1.4(@csstools/css-parser-algorithms@2.3.2)(@csstools/css-tokenizer@2.2.1) - '@csstools/css-parser-algorithms': 2.3.2(@csstools/css-tokenizer@2.2.1) - '@csstools/css-tokenizer': 2.2.1 - postcss: 8.4.31 - dev: false - /@csstools/postcss-font-format-keywords@3.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-ntkGj+1uDa/u6lpjPxnkPcjJn7ChO/Kcy08YxctOZI7vwtrdYvFhmE476dq8bj1yna306+jQ9gzXIG/SWfOaRg==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-font-format-keywords@3.0.2': + resolution: {integrity: sha512-E0xz2sjm4AMCkXLCFvI/lyl4XO6aN1NCSMMVEOngFDJ+k2rDwfr6NDjWljk1li42jiLNChVX+YFnmfGCigZKXw==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - postcss: 8.4.31 - postcss-value-parser: 4.2.0 - dev: false - /@csstools/postcss-gamut-mapping@1.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-6UQyK8l9YaG5Ao5rBDcCnKHrLsHiQ1E0zeFQuqDJqEtinVzAPb/MwSw3TenZXL1Rnd7th3tb+4CBFHBXdW5tbQ==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-gamut-mapping@1.0.10': + resolution: {integrity: sha512-iPz4/cO8YiNjAYdtAiKGBdKZdFlAvDtUr2AgvAMxCa83e9MwTIKmsJZC3Frw7VYmkfknmdElEZr1FJU+PmB2PA==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - '@csstools/css-color-parser': 1.4.0(@csstools/css-parser-algorithms@2.3.2)(@csstools/css-tokenizer@2.2.1) - '@csstools/css-parser-algorithms': 2.3.2(@csstools/css-tokenizer@2.2.1) - '@csstools/css-tokenizer': 2.2.1 - postcss: 8.4.31 - dev: false - /@csstools/postcss-gradients-interpolation-method@4.0.7(postcss@8.4.31): - resolution: - { - integrity: sha512-GT1CzE/Tyr/ei4j5BwKESkHAgg+Gzys/0mAY7W+UiR+XrcYk5hDbOrE/YJIx1rflfO/7La1bDoZtA0YnLl4qNA==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-gradients-interpolation-method@4.0.18': + resolution: {integrity: sha512-rZH7RnNYY911I/n8+DRrcri89GffptdyuFDGGj/UbxDISFirdR1uI/wcur9KYR/uFHXqrnJjrfi1cisfB7bL+g==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - '@csstools/css-color-parser': 1.4.0(@csstools/css-parser-algorithms@2.3.2)(@csstools/css-tokenizer@2.2.1) - '@csstools/css-parser-algorithms': 2.3.2(@csstools/css-tokenizer@2.2.1) - '@csstools/css-tokenizer': 2.2.1 - '@csstools/postcss-progressive-custom-properties': 3.0.2(postcss@8.4.31) - postcss: 8.4.31 - dev: false - /@csstools/postcss-hwb-function@3.0.6(postcss@8.4.31): - resolution: - { - integrity: sha512-uQgWt2Ho2yy2S6qthWY7mD5v57NKxi6dD1NB8nAybU5bJSsm+hLXRGm3/zbOH4xNrqO3Cl60DFSNlSrUME3Xjg==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-hwb-function@3.0.16': + resolution: {integrity: sha512-nlC4D5xB7pomgR4kDZ1lqbVqrs6gxPqsM2OE5CkCn0EqCMxtqqtadtbK2dcFwzyujv3DL4wYNo+fgF4rJgLPZA==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - '@csstools/css-color-parser': 1.4.0(@csstools/css-parser-algorithms@2.3.2)(@csstools/css-tokenizer@2.2.1) - '@csstools/css-parser-algorithms': 2.3.2(@csstools/css-tokenizer@2.2.1) - '@csstools/css-tokenizer': 2.2.1 - postcss: 8.4.31 - dev: false - /@csstools/postcss-ic-unit@3.0.2(postcss@8.4.31): - resolution: - { - integrity: sha512-n28Er7W9qc48zNjJnvTKuVHY26/+6YlA9WzJRksIHiAWOMxSH5IksXkw7FpkIOd+jLi59BMrX/BWrZMgjkLBHg==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-ic-unit@3.0.6': + resolution: {integrity: sha512-fHaU9C/sZPauXMrzPitZ/xbACbvxbkPpHoUgB9Kw5evtsBWdVkVrajOyiT9qX7/c+G1yjApoQjP1fQatldsy9w==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - '@csstools/postcss-progressive-custom-properties': 3.0.2(postcss@8.4.31) - postcss: 8.4.31 - postcss-value-parser: 4.2.0 - dev: false - /@csstools/postcss-initial@1.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-1l7iHHjIl5qmVeGItugr4ZOlCREDP71mNKqoEyxlosIoiu3Os1nPWMHpuCvDLCLiWI/ONTOg3nzJh7gwHOrqUA==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-initial@1.0.1': + resolution: {integrity: sha512-wtb+IbUIrIf8CrN6MLQuFR7nlU5C7PwuebfeEXfjthUha1+XZj2RVi+5k/lukToA24sZkYAiSJfHM8uG/UZIdg==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - postcss: 8.4.31 - dev: false - /@csstools/postcss-is-pseudo-class@4.0.3(postcss@8.4.31): - resolution: - { - integrity: sha512-/dt5M9Ty/x3Yiq0Nm/5PJJzwkVFchJgdjKVnryBPtoMCb9ohb/nDIJOwr/Wr3hK3FDs1EA1GE6PyRYsUmQPS8Q==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-is-pseudo-class@4.0.8': + resolution: {integrity: sha512-0aj591yGlq5Qac+plaWCbn5cpjs5Sh0daovYUKJUOMjIp70prGH/XPLp7QjxtbFXz3CTvb0H9a35dpEuIuUi3Q==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - '@csstools/selector-specificity': 3.0.0(postcss-selector-parser@6.0.13) - postcss: 8.4.31 - postcss-selector-parser: 6.0.13 - dev: false - /@csstools/postcss-logical-float-and-clear@2.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-Wki4vxsF6icRvRz8eF9bPpAvwaAt0RHwhVOyzfoFg52XiIMjb6jcbHkGxwpJXP4DVrnFEwpwmrz5aTRqOW82kg==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-light-dark-function@1.0.6': + resolution: {integrity: sha512-bu+cxKpcTrMDMkVCv7QURwKNPZEuXA3J0Udvz3HfmQHt4+OIvvfvDpTgejFXdOliCU4zK9/QdqebPcYneygZtg==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - postcss: 8.4.31 - dev: false - /@csstools/postcss-logical-overflow@1.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-cIrZ8f7bGGvr+W53nEuMspcwaeaI2YTmz6LZ4yiAO5z14/PQgOOv+Pn+qjvPOPoadeY2BmpaoTzZKvdAQuM17w==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-logical-float-and-clear@2.0.1': + resolution: {integrity: sha512-SsrWUNaXKr+e/Uo4R/uIsqJYt3DaggIh/jyZdhy/q8fECoJSKsSMr7nObSLdvoULB69Zb6Bs+sefEIoMG/YfOA==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - postcss: 8.4.31 - dev: false - /@csstools/postcss-logical-overscroll-behavior@1.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-e89S2LWjnxf0SB2wNUAbqDyFb/Fow/tlOe1XqOLbNx4rf3LrQokM9qldVx7sarnddml3ORE5LDUmlKpPOOeJTA==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-logical-overflow@1.0.1': + resolution: {integrity: sha512-Kl4lAbMg0iyztEzDhZuQw8Sj9r2uqFDcU1IPl+AAt2nue8K/f1i7ElvKtXkjhIAmKiy5h2EY8Gt/Cqg0pYFDCw==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - postcss: 8.4.31 - dev: false - /@csstools/postcss-logical-resize@2.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-lCQ1aX8c5+WI4t5EoYf3alTzJNNocMqTb+u1J9CINdDhFh1fjovqK+0aHalUHsNstZmzFPNzIkU4Mb3eM9U8SA==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-logical-overscroll-behavior@1.0.1': + resolution: {integrity: sha512-+kHamNxAnX8ojPCtV8WPcUP3XcqMFBSDuBuvT6MHgq7oX4IQxLIXKx64t7g9LiuJzE7vd06Q9qUYR6bh4YnGpQ==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - postcss: 8.4.31 - postcss-value-parser: 4.2.0 - dev: false - /@csstools/postcss-logical-viewport-units@2.0.3(postcss@8.4.31): - resolution: - { - integrity: sha512-xeVxqND5rlQyqLGdH7rX34sIm/JbbQKxpKQP8oD1YQqUHHCLQR9NUS57WqJKajxKN6AcNAMWJhb5LUH5RfPcyA==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-logical-resize@2.0.1': + resolution: {integrity: sha512-W5Gtwz7oIuFcKa5SmBjQ2uxr8ZoL7M2bkoIf0T1WeNqljMkBrfw1DDA8/J83k57NQ1kcweJEjkJ04pUkmyee3A==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - '@csstools/css-tokenizer': 2.2.1 - postcss: 8.4.31 - dev: false - /@csstools/postcss-media-minmax@1.1.0(postcss@8.4.31): - resolution: - { - integrity: sha512-t5Li/DPC5QmW/6VFLfUvsw/4dNYYseWR0tOXDeJg/9EKUodBgNawz5tuk5vYKtNvoj+Q08odMuXcpS5YJj0AFA==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-logical-viewport-units@2.0.10': + resolution: {integrity: sha512-nGP0KanI/jXrUMpaIBz6mdy/vNs3d/cjbNYuoEc7lCdNkntmxZvwxC2zIKI8QzGWaYsh9jahozMVceZ0jNyjgg==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - '@csstools/css-calc': 1.1.4(@csstools/css-parser-algorithms@2.3.2)(@csstools/css-tokenizer@2.2.1) - '@csstools/css-parser-algorithms': 2.3.2(@csstools/css-tokenizer@2.2.1) - '@csstools/css-tokenizer': 2.2.1 - '@csstools/media-query-list-parser': 2.1.5(@csstools/css-parser-algorithms@2.3.2)(@csstools/css-tokenizer@2.2.1) - postcss: 8.4.31 - dev: false - /@csstools/postcss-media-queries-aspect-ratio-number-values@2.0.3(postcss@8.4.31): - resolution: - { - integrity: sha512-IPL8AvnwMYW+cWtp+j8cW3MFN0RyXNT4hLOvs6Rf2N+NcbvXhSyKxZuE3W9Cv4KjaNoNoGx1d0UhT6tktq6tUw==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-media-minmax@1.1.7': + resolution: {integrity: sha512-AjLG+vJvhrN2geUjYNvzncW1TJ+vC4QrVPGrLPxOSJ2QXC94krQErSW4aXMj0b13zhvVWeqf2NHIOVQknqV9cg==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - '@csstools/css-parser-algorithms': 2.3.2(@csstools/css-tokenizer@2.2.1) - '@csstools/css-tokenizer': 2.2.1 - '@csstools/media-query-list-parser': 2.1.5(@csstools/css-parser-algorithms@2.3.2)(@csstools/css-tokenizer@2.2.1) - postcss: 8.4.31 - dev: false - /@csstools/postcss-nested-calc@3.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-HsB66aDWAouOwD/GcfDTS0a7wCuVWaTpXcjl5VKP0XvFxDiU+r0T8FG7xgb6ovZNZ+qzvGIwRM+CLHhDgXrYgQ==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-media-queries-aspect-ratio-number-values@2.0.10': + resolution: {integrity: sha512-DXae3i7OYJTejxcoUuf/AOIpy+6FWfGGKo/I3WefZI538l3k+ErU6V2xQOx/UmUXT2FDIdE1Ucl9JkZib2rEsA==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - postcss: 8.4.31 - postcss-value-parser: 4.2.0 - dev: false - /@csstools/postcss-normalize-display-values@3.0.1(postcss@8.4.31): - resolution: - { - integrity: sha512-nUvRxI+ALJwkxZdPU4EDyuM380vP91sAGvI3jAOHs/sr3jfcCOzLkY6xKI1Mr526kZ3RivmMoYM/xq+XFyE/bw==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-nested-calc@3.0.2': + resolution: {integrity: sha512-ySUmPyawiHSmBW/VI44+IObcKH0v88LqFe0d09Sb3w4B1qjkaROc6d5IA3ll9kjD46IIX/dbO5bwFN/swyoyZA==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - postcss: 8.4.31 - postcss-value-parser: 4.2.0 - dev: false - /@csstools/postcss-oklab-function@3.0.7(postcss@8.4.31): - resolution: - { - integrity: sha512-vBFTQD3CARB3u/XIGO44wWbcO7xG/4GsYqJlcPuUGRSK8mtxes6n4vvNFlIByyAZy2k4d4RY63nyvTbMpeNTaQ==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-normalize-display-values@3.0.2': + resolution: {integrity: sha512-fCapyyT/dUdyPtrelQSIV+d5HqtTgnNP/BEG9IuhgXHt93Wc4CfC1bQ55GzKAjWrZbgakMQ7MLfCXEf3rlZJOw==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - '@csstools/css-color-parser': 1.4.0(@csstools/css-parser-algorithms@2.3.2)(@csstools/css-tokenizer@2.2.1) - '@csstools/css-parser-algorithms': 2.3.2(@csstools/css-tokenizer@2.2.1) - '@csstools/css-tokenizer': 2.2.1 - '@csstools/postcss-progressive-custom-properties': 3.0.2(postcss@8.4.31) - postcss: 8.4.31 - dev: false - /@csstools/postcss-progressive-custom-properties@3.0.2(postcss@8.4.31): - resolution: - { - integrity: sha512-YEvTozk1SxnV/PGL5DllBVDuLQ+jiQhyCSQiZJ6CwBMU5JQ9hFde3i1qqzZHuclZfptjrU0JjlX4ePsOhxNzHw==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-oklab-function@3.0.17': + resolution: {integrity: sha512-kIng3Xmw6NKUvD/eEoHGwbyDFXDsuzsVGtNo3ndgZYYqy+DLiD+3drxwRKiViE5LUieLB1ERczXpLVmpSw61eg==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - postcss: 8.4.31 - postcss-value-parser: 4.2.0 - dev: false - /@csstools/postcss-relative-color-syntax@2.0.7(postcss@8.4.31): - resolution: - { - integrity: sha512-2AiFbJSVF4EyymLxme4JzSrbXykHolx8DdZECHjYKMhoulhKLltx5ccYgtrK3BmXGd3v3nJrWFCc8JM8bjuiOg==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-progressive-custom-properties@3.2.0': + resolution: {integrity: sha512-BZlirVxCRgKlE7yVme+Xvif72eTn1MYXj8oZ4Knb+jwaH4u3AN1DjbhM7j86RP5vvuAOexJ4JwfifYYKWMN/QQ==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - '@csstools/css-color-parser': 1.4.0(@csstools/css-parser-algorithms@2.3.2)(@csstools/css-tokenizer@2.2.1) - '@csstools/css-parser-algorithms': 2.3.2(@csstools/css-tokenizer@2.2.1) - '@csstools/css-tokenizer': 2.2.1 - '@csstools/postcss-progressive-custom-properties': 3.0.2(postcss@8.4.31) - postcss: 8.4.31 - dev: false - /@csstools/postcss-scope-pseudo-class@3.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-GFNVsD97OuEcfHmcT0/DAZWAvTM/FFBDQndIOLawNc1Wq8YqpZwBdHa063Lq+Irk7azygTT+Iinyg3Lt76p7rg==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-relative-color-syntax@2.0.17': + resolution: {integrity: sha512-EVckAtG8bocItZflXLJ50Su+gwg/4Jhkz1BztyNsT0/svwS6QMAeLjyUA75OsgtejNWQHvBMWna4xc9LCqdjrQ==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - postcss: 8.4.31 - postcss-selector-parser: 6.0.13 - dev: false - /@csstools/postcss-stepped-value-functions@3.0.2(postcss@8.4.31): - resolution: - { - integrity: sha512-I3wX44MZVv+tDuWfrd3BTvRB/YRIM2F5v1MBtTI89sxpFn47mNpTwpPYUOGPVCgKlRDfZSlxIUYhUQmqRQZZFQ==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-scope-pseudo-class@3.0.1': + resolution: {integrity: sha512-3ZFonK2gfgqg29gUJ2w7xVw2wFJ1eNWVDONjbzGkm73gJHVCYK5fnCqlLr+N+KbEfv2XbWAO0AaOJCFB6Fer6A==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - '@csstools/css-calc': 1.1.4(@csstools/css-parser-algorithms@2.3.2)(@csstools/css-tokenizer@2.2.1) - '@csstools/css-parser-algorithms': 2.3.2(@csstools/css-tokenizer@2.2.1) - '@csstools/css-tokenizer': 2.2.1 - postcss: 8.4.31 - dev: false - /@csstools/postcss-text-decoration-shorthand@3.0.3(postcss@8.4.31): - resolution: - { - integrity: sha512-d5J9m49HhqXRcw1S6vTZuviHi/iknUKGjBpChiNK1ARg9sSa3b8m5lsWz5Izs8ISORZdv2bZRwbw5Z2R6gQ9kQ==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-stepped-value-functions@3.0.9': + resolution: {integrity: sha512-uAw1J8hiZ0mM1DLaziI7CP5oagSwDnS5kufuROGIJFzESYfTqNVS3b7FgDZto9AxXdkwI+Sn48+cvG8PwzGMog==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - '@csstools/color-helpers': 3.0.2 - postcss: 8.4.31 - postcss-value-parser: 4.2.0 - dev: false - /@csstools/postcss-trigonometric-functions@3.0.2(postcss@8.4.31): - resolution: - { - integrity: sha512-AwzNhF4QOKaLOKvMljwwFkeYXwufhRO15G+kKohHkyoNOL75xWkN+W2Y9ik9tSeAyDv+cYNlYaF+o/a79WjVjg==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-text-decoration-shorthand@3.0.7': + resolution: {integrity: sha512-+cptcsM5r45jntU6VjotnkC9GteFR7BQBfZ5oW7inLCxj7AfLGAzMbZ60hKTP13AULVZBdxky0P8um0IBfLHVA==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - '@csstools/css-calc': 1.1.4(@csstools/css-parser-algorithms@2.3.2)(@csstools/css-tokenizer@2.2.1) - '@csstools/css-parser-algorithms': 2.3.2(@csstools/css-tokenizer@2.2.1) - '@csstools/css-tokenizer': 2.2.1 - postcss: 8.4.31 - dev: false - /@csstools/postcss-unset-value@3.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-P0JD1WHh3avVyKKRKjd0dZIjCEeaBer8t1BbwGMUDtSZaLhXlLNBqZ8KkqHzYWXOJgHleXAny2/sx8LYl6qhEA==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-trigonometric-functions@3.0.9': + resolution: {integrity: sha512-rCAtKX3EsH91ZIHoxFzAAcMQeQCS+PsjzHl6fvsGXz/SV3lqzSmO7MWgFXyPktC2zjZXgOObAJ/2QkhMqVpgNg==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss: ^8.4 - dependencies: - postcss: 8.4.31 - dev: false - /@csstools/selector-specificity@3.0.0(postcss-selector-parser@6.0.13): - resolution: - { - integrity: sha512-hBI9tfBtuPIi885ZsZ32IMEU/5nlZH/KOVYJCOh7gyMxaVLGmLedYqFN6Ui1LXkI8JlC8IsuC0rF0btcRZKd5g==, - } - engines: { node: ^14 || ^16 || >=18 } + '@csstools/postcss-unset-value@3.0.1': + resolution: {integrity: sha512-dbDnZ2ja2U8mbPP0Hvmt2RMEGBiF1H7oY6HYSpjteXJGihYwgxgTr6KRbbJ/V6c+4wd51M+9980qG4gKVn5ttg==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + + '@csstools/selector-resolve-nested@1.1.0': + resolution: {integrity: sha512-uWvSaeRcHyeNenKg8tp17EVDRkpflmdyvbE0DHo6D/GdBb6PDnCYYU6gRpXhtICMGMcahQmj2zGxwFM/WC8hCg==} + engines: {node: ^14 || ^16 || >=18} peerDependencies: postcss-selector-parser: ^6.0.13 - dependencies: - postcss-selector-parser: 6.0.13 - /@discoveryjs/json-ext@0.5.7: - resolution: - { - integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==, - } - engines: { node: '>=10.0.0' } - dev: false - - /@esbuild/android-arm64@0.18.20: - resolution: - { - integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==, - } - engines: { node: '>=12' } + '@csstools/selector-specificity@3.0.0': + resolution: {integrity: sha512-hBI9tfBtuPIi885ZsZ32IMEU/5nlZH/KOVYJCOh7gyMxaVLGmLedYqFN6Ui1LXkI8JlC8IsuC0rF0btcRZKd5g==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss-selector-parser: ^6.0.13 + + '@csstools/selector-specificity@3.1.1': + resolution: {integrity: sha512-a7cxGcJ2wIlMFLlh8z2ONm+715QkPHiyJcxwQlKOz/03GPw1COpfhcmC9wm4xlZfp//jWHNNMwzjtqHXVWU9KA==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss-selector-parser: ^6.0.13 + + '@csstools/utilities@1.0.0': + resolution: {integrity: sha512-tAgvZQe/t2mlvpNosA4+CkMiZ2azISW5WPAcdSalZlEjQvUfghHxfQcrCiK/7/CrfAWVxyM88kGFYO82heIGDg==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + + '@discoveryjs/json-ext@0.5.7': + resolution: {integrity: sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw==} + engines: {node: '>=10.0.0'} + + '@emnapi/core@1.5.0': + resolution: {integrity: sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==} + + '@emnapi/runtime@1.5.0': + resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==} + + '@emnapi/wasi-threads@1.1.0': + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + + '@esbuild/android-arm64@0.18.20': + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} cpu: [arm64] os: [android] - requiresBuild: true - dev: true - optional: true - /@esbuild/android-arm@0.18.20: - resolution: - { - integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==, - } - engines: { node: '>=12' } + '@esbuild/android-arm@0.18.20': + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} cpu: [arm] os: [android] - requiresBuild: true - dev: true - optional: true - /@esbuild/android-x64@0.18.20: - resolution: - { - integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==, - } - engines: { node: '>=12' } + '@esbuild/android-x64@0.18.20': + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} cpu: [x64] os: [android] - requiresBuild: true - dev: true - optional: true - /@esbuild/darwin-arm64@0.18.20: - resolution: - { - integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==, - } - engines: { node: '>=12' } + '@esbuild/darwin-arm64@0.18.20': + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} cpu: [arm64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@esbuild/darwin-x64@0.18.20: - resolution: - { - integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==, - } - engines: { node: '>=12' } + '@esbuild/darwin-x64@0.18.20': + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} cpu: [x64] os: [darwin] - requiresBuild: true - dev: true - optional: true - /@esbuild/freebsd-arm64@0.18.20: - resolution: - { - integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==, - } - engines: { node: '>=12' } + '@esbuild/freebsd-arm64@0.18.20': + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} cpu: [arm64] os: [freebsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/freebsd-x64@0.18.20: - resolution: - { - integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==, - } - engines: { node: '>=12' } + '@esbuild/freebsd-x64@0.18.20': + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} cpu: [x64] os: [freebsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-arm64@0.18.20: - resolution: - { - integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==, - } - engines: { node: '>=12' } + '@esbuild/linux-arm64@0.18.20': + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} cpu: [arm64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-arm@0.18.20: - resolution: - { - integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==, - } - engines: { node: '>=12' } + '@esbuild/linux-arm@0.18.20': + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} cpu: [arm] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-ia32@0.18.20: - resolution: - { - integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==, - } - engines: { node: '>=12' } + '@esbuild/linux-ia32@0.18.20': + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} cpu: [ia32] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-loong64@0.18.20: - resolution: - { - integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==, - } - engines: { node: '>=12' } + '@esbuild/linux-loong64@0.18.20': + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} cpu: [loong64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-mips64el@0.18.20: - resolution: - { - integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==, - } - engines: { node: '>=12' } + '@esbuild/linux-mips64el@0.18.20': + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} cpu: [mips64el] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-ppc64@0.18.20: - resolution: - { - integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==, - } - engines: { node: '>=12' } + '@esbuild/linux-ppc64@0.18.20': + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} cpu: [ppc64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-riscv64@0.18.20: - resolution: - { - integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==, - } - engines: { node: '>=12' } + '@esbuild/linux-riscv64@0.18.20': + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} cpu: [riscv64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-s390x@0.18.20: - resolution: - { - integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==, - } - engines: { node: '>=12' } + '@esbuild/linux-s390x@0.18.20': + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} cpu: [s390x] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/linux-x64@0.18.20: - resolution: - { - integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==, - } - engines: { node: '>=12' } + '@esbuild/linux-x64@0.18.20': + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} cpu: [x64] os: [linux] - requiresBuild: true - dev: true - optional: true - /@esbuild/netbsd-x64@0.18.20: - resolution: - { - integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==, - } - engines: { node: '>=12' } + '@esbuild/netbsd-x64@0.18.20': + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} cpu: [x64] os: [netbsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/openbsd-x64@0.18.20: - resolution: - { - integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==, - } - engines: { node: '>=12' } + '@esbuild/openbsd-x64@0.18.20': + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} cpu: [x64] os: [openbsd] - requiresBuild: true - dev: true - optional: true - /@esbuild/sunos-x64@0.18.20: - resolution: - { - integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==, - } - engines: { node: '>=12' } + '@esbuild/sunos-x64@0.18.20': + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} cpu: [x64] os: [sunos] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-arm64@0.18.20: - resolution: - { - integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==, - } - engines: { node: '>=12' } + '@esbuild/win32-arm64@0.18.20': + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} cpu: [arm64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-ia32@0.18.20: - resolution: - { - integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==, - } - engines: { node: '>=12' } + '@esbuild/win32-ia32@0.18.20': + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} cpu: [ia32] os: [win32] - requiresBuild: true - dev: true - optional: true - /@esbuild/win32-x64@0.18.20: - resolution: - { - integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==, - } - engines: { node: '>=12' } + '@esbuild/win32-x64@0.18.20': + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} cpu: [x64] os: [win32] - requiresBuild: true - dev: true - optional: true - /@eslint-community/eslint-utils@4.4.0(eslint@8.53.0): - resolution: - { - integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + '@eslint-community/eslint-utils@4.4.0': + resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 - dependencies: - eslint: 8.53.0 - eslint-visitor-keys: 3.4.3 - dev: true - - /@eslint-community/regexpp@4.9.0: - resolution: - { - integrity: sha512-zJmuCWj2VLBt4c25CfBIbMZLGLyhkvs7LznyVX5HfpzeocThgIj5XQK4L+g3U36mMcx8bPMhGyPpwCATamC4jQ==, - } - engines: { node: ^12.0.0 || ^14.0.0 || >=16.0.0 } - dev: true - - /@eslint/eslintrc@2.1.3: - resolution: - { - integrity: sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } - dependencies: - ajv: 6.12.6 - debug: 4.3.4 - espree: 9.6.1 - globals: 13.22.0 - ignore: 5.2.4 - import-fresh: 3.3.0 - js-yaml: 4.1.0 - minimatch: 3.1.2 - strip-json-comments: 3.1.1 - transitivePeerDependencies: - - supports-color - dev: true - - /@eslint/js@8.53.0: - resolution: - { - integrity: sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } - dev: true - - /@fastify/busboy@1.2.1: - resolution: - { - integrity: sha512-7PQA7EH43S0CxcOa9OeAnaeA0oQ+e/DHNPZwSQM9CQHW76jle5+OvLdibRp/Aafs9KXbLhxyjOTkRjWUbQEd3Q==, - } - engines: { node: '>=14' } - requiresBuild: true - dependencies: - text-decoding: 1.0.0 - dev: false - optional: true - /@firebase/analytics-compat@0.2.6(@firebase/app-compat@0.2.13)(@firebase/app@0.9.13): - resolution: - { - integrity: sha512-4MqpVLFkGK7NJf/5wPEEP7ePBJatwYpyjgJ+wQHQGHfzaCDgntOnl9rL2vbVGGKCnRqWtZDIWhctB86UWXaX2Q==, - } + '@eslint-community/eslint-utils@4.7.0': + resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.9.0': + resolution: {integrity: sha512-zJmuCWj2VLBt4c25CfBIbMZLGLyhkvs7LznyVX5HfpzeocThgIj5XQK4L+g3U36mMcx8bPMhGyPpwCATamC4jQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/eslintrc@2.1.4': + resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@eslint/js@8.57.1': + resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@fastify/busboy@1.2.1': + resolution: {integrity: sha512-7PQA7EH43S0CxcOa9OeAnaeA0oQ+e/DHNPZwSQM9CQHW76jle5+OvLdibRp/Aafs9KXbLhxyjOTkRjWUbQEd3Q==} + engines: {node: '>=14'} + + '@firebase/analytics-compat@0.2.14': + resolution: {integrity: sha512-unRVY6SvRqfNFIAA/kwl4vK+lvQAL2HVcgu9zTrUtTyYDmtIt/lOuHJynBMYEgLnKm39YKBDhtqdapP2e++ASw==} peerDependencies: '@firebase/app-compat': 0.x - dependencies: - '@firebase/analytics': 0.10.0(@firebase/app@0.9.13) - '@firebase/analytics-types': 0.8.0 - '@firebase/app-compat': 0.2.13 - '@firebase/component': 0.6.4 - '@firebase/util': 1.9.3 - tslib: 2.6.2 - transitivePeerDependencies: - - '@firebase/app' - dev: false - /@firebase/analytics-types@0.8.0: - resolution: - { - integrity: sha512-iRP+QKI2+oz3UAh4nPEq14CsEjrjD6a5+fuypjScisAh9kXKFvdJOZJDwk7kikLvWVLGEs9+kIUS4LPQV7VZVw==, - } - dev: false + '@firebase/analytics-types@0.8.2': + resolution: {integrity: sha512-EnzNNLh+9/sJsimsA/FGqzakmrAUKLeJvjRHlg8df1f97NLUlFidk9600y0ZgWOp3CAxn6Hjtk+08tixlUOWyw==} - /@firebase/analytics@0.10.0(@firebase/app@0.9.13): - resolution: - { - integrity: sha512-Locv8gAqx0e+GX/0SI3dzmBY5e9kjVDtD+3zCFLJ0tH2hJwuCAiL+5WkHuxKj92rqQj/rvkBUCfA1ewlX2hehg==, - } + '@firebase/analytics@0.10.8': + resolution: {integrity: sha512-CVnHcS4iRJPqtIDc411+UmFldk0ShSK3OB+D0bKD8Ck5Vro6dbK5+APZpkuWpbfdL359DIQUnAaMLE+zs/PVyA==} peerDependencies: '@firebase/app': 0.x - dependencies: - '@firebase/app': 0.9.13 - '@firebase/component': 0.6.4 - '@firebase/installations': 0.6.4(@firebase/app@0.9.13) - '@firebase/logger': 0.4.0 - '@firebase/util': 1.9.3 - tslib: 2.6.2 - dev: false - /@firebase/app-check-compat@0.3.7(@firebase/app-compat@0.2.13)(@firebase/app@0.9.13): - resolution: - { - integrity: sha512-cW682AxsyP1G+Z0/P7pO/WT2CzYlNxoNe5QejVarW2o5ZxeWSSPAiVEwpEpQR/bUlUmdeWThYTMvBWaopdBsqw==, - } + '@firebase/app-check-compat@0.3.15': + resolution: {integrity: sha512-zFIvIFFNqDXpOT2huorz9cwf56VT3oJYRFjSFYdSbGYEJYEaXjLJbfC79lx/zjx4Fh+yuN8pry3TtvwaevrGbg==} peerDependencies: '@firebase/app-compat': 0.x - dependencies: - '@firebase/app-check': 0.8.0(@firebase/app@0.9.13) - '@firebase/app-check-types': 0.5.0 - '@firebase/app-compat': 0.2.13 - '@firebase/component': 0.6.4 - '@firebase/logger': 0.4.0 - '@firebase/util': 1.9.3 - tslib: 2.6.2 - transitivePeerDependencies: - - '@firebase/app' - dev: false - - /@firebase/app-check-interop-types@0.3.0: - resolution: - { - integrity: sha512-xAxHPZPIgFXnI+vb4sbBjZcde7ZluzPPaSK7Lx3/nmuVk4TjZvnL8ONnkd4ERQKL8WePQySU+pRcWkh8rDf5Sg==, - } - dev: false - - /@firebase/app-check-types@0.5.0: - resolution: - { - integrity: sha512-uwSUj32Mlubybw7tedRzR24RP8M8JUVR3NPiMk3/Z4bCmgEKTlQBwMXrehDAZ2wF+TsBq0SN1c6ema71U/JPyQ==, - } - dev: false - - /@firebase/app-check@0.8.0(@firebase/app@0.9.13): - resolution: - { - integrity: sha512-dRDnhkcaC2FspMiRK/Vbp+PfsOAEP6ZElGm9iGFJ9fDqHoPs0HOPn7dwpJ51lCFi1+2/7n5pRPGhqF/F03I97g==, - } + + '@firebase/app-check-interop-types@0.3.2': + resolution: {integrity: sha512-LMs47Vinv2HBMZi49C09dJxp0QT5LwDzFaVGf/+ITHe3BlIhUiLNttkATSXplc89A2lAaeTqjgqVkiRfUGyQiQ==} + + '@firebase/app-check-types@0.5.2': + resolution: {integrity: sha512-FSOEzTzL5bLUbD2co3Zut46iyPWML6xc4x+78TeaXMSuJap5QObfb+rVvZJtla3asN4RwU7elaQaduP+HFizDA==} + + '@firebase/app-check@0.8.8': + resolution: {integrity: sha512-O49RGF1xj7k6BuhxGpHmqOW5hqBIAEbt2q6POW0lIywx7emYtzPDeQI+ryQpC4zbKX646SoVZ711TN1DBLNSOQ==} peerDependencies: '@firebase/app': 0.x - dependencies: - '@firebase/app': 0.9.13 - '@firebase/component': 0.6.4 - '@firebase/logger': 0.4.0 - '@firebase/util': 1.9.3 - tslib: 2.6.2 - dev: false - - /@firebase/app-compat@0.2.13: - resolution: - { - integrity: sha512-j6ANZaWjeVy5zg6X7uiqh6lM6o3n3LD1+/SJFNs9V781xyryyZWXe+tmnWNWPkP086QfJoNkWN9pMQRqSG4vMg==, - } - dependencies: - '@firebase/app': 0.9.13 - '@firebase/component': 0.6.4 - '@firebase/logger': 0.4.0 - '@firebase/util': 1.9.3 - tslib: 2.6.2 - dev: false - - /@firebase/app-types@0.8.1: - resolution: - { - integrity: sha512-p75Ow3QhB82kpMzmOntv866wH9eZ3b4+QbUY+8/DA5Zzdf1c8Nsk8B7kbFpzJt4wwHMdy5LTF5YUnoTc1JiWkw==, - } - requiresBuild: true - dev: false - optional: true - /@firebase/app-types@0.9.0: - resolution: - { - integrity: sha512-AeweANOIo0Mb8GiYm3xhTEBVCmPwTYAu9Hcd2qSkLuga/6+j9b1Jskl5bpiSQWy9eJ/j5pavxj6eYogmnuzm+Q==, - } - dev: false - - /@firebase/app@0.9.13: - resolution: - { - integrity: sha512-GfiI1JxJ7ecluEmDjPzseRXk/PX31hS7+tjgBopL7XjB2hLUdR+0FTMXy2Q3/hXezypDvU6or7gVFizDESrkXw==, - } - dependencies: - '@firebase/component': 0.6.4 - '@firebase/logger': 0.4.0 - '@firebase/util': 1.9.3 - idb: 7.1.1 - tslib: 2.6.2 - dev: false + '@firebase/app-compat@0.2.43': + resolution: {integrity: sha512-HM96ZyIblXjAC7TzE8wIk2QhHlSvksYkQ4Ukh1GmEenzkucSNUmUX4QvoKrqeWsLEQ8hdcojABeCV8ybVyZmeg==} + + '@firebase/app-types@0.8.1': + resolution: {integrity: sha512-p75Ow3QhB82kpMzmOntv866wH9eZ3b4+QbUY+8/DA5Zzdf1c8Nsk8B7kbFpzJt4wwHMdy5LTF5YUnoTc1JiWkw==} + + '@firebase/app-types@0.9.2': + resolution: {integrity: sha512-oMEZ1TDlBz479lmABwWsWjzHwheQKiAgnuKxE0pz0IXCVx7/rtlkx1fQ6GfgK24WCrxDKMplZrT50Kh04iMbXQ==} - /@firebase/auth-compat@0.4.2(@firebase/app-compat@0.2.13)(@firebase/app-types@0.9.0)(@firebase/app@0.9.13): - resolution: - { - integrity: sha512-Q30e77DWXFmXEt5dg5JbqEDpjw9y3/PcP9LslDPR7fARmAOTIY9MM6HXzm9KC+dlrKH/+p6l8g9ifJiam9mc4A==, - } + '@firebase/app@0.10.13': + resolution: {integrity: sha512-OZiDAEK/lDB6xy/XzYAyJJkaDqmQ+BCtOEPLqFvxWKUz5JbBmej7IiiRHdtiIOD/twW7O5AxVsfaaGA/V1bNsA==} + + '@firebase/auth-compat@0.5.14': + resolution: {integrity: sha512-2eczCSqBl1KUPJacZlFpQayvpilg3dxXLy9cSMTKtQMTQSmondUtPI47P3ikH3bQAXhzKLOE+qVxJ3/IRtu9pw==} peerDependencies: '@firebase/app-compat': 0.x - dependencies: - '@firebase/app-compat': 0.2.13 - '@firebase/auth': 0.23.2(@firebase/app@0.9.13) - '@firebase/auth-types': 0.12.0(@firebase/app-types@0.9.0)(@firebase/util@1.9.3) - '@firebase/component': 0.6.4 - '@firebase/util': 1.9.3 - node-fetch: 2.6.7 - tslib: 2.6.2 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/app-types' - - encoding - dev: false - /@firebase/auth-interop-types@0.1.7(@firebase/app-types@0.9.0)(@firebase/util@1.7.3): - resolution: - { - integrity: sha512-yA/dTveGGPcc85JP8ZE/KZqfGQyQTBCV10THdI8HTlP1GDvNrhr//J5jAt58MlsCOaO3XmC4DqScPBbtIsR/EA==, - } - requiresBuild: true + '@firebase/auth-interop-types@0.1.7': + resolution: {integrity: sha512-yA/dTveGGPcc85JP8ZE/KZqfGQyQTBCV10THdI8HTlP1GDvNrhr//J5jAt58MlsCOaO3XmC4DqScPBbtIsR/EA==} peerDependencies: '@firebase/app-types': 0.x '@firebase/util': 1.x - dependencies: - '@firebase/app-types': 0.9.0 - '@firebase/util': 1.7.3 - dev: false - optional: true - /@firebase/auth-interop-types@0.2.1: - resolution: - { - integrity: sha512-VOaGzKp65MY6P5FI84TfYKBXEPi6LmOCSMMzys6o2BN2LOsqy7pCuZCup7NYnfbk5OkkQKzvIfHOzTm0UDpkyg==, - } - dev: false + '@firebase/auth-interop-types@0.2.3': + resolution: {integrity: sha512-Fc9wuJGgxoxQeavybiuwgyi+0rssr76b+nHpj+eGhXFYAdudMWyfBHvFL/I5fEHniUM/UQdFzi9VXJK2iZF7FQ==} - /@firebase/auth-types@0.12.0(@firebase/app-types@0.9.0)(@firebase/util@1.9.3): - resolution: - { - integrity: sha512-pPwaZt+SPOshK8xNoiQlK5XIrS97kFYc3Rc7xmy373QsOJ9MmqXxLaYssP5Kcds4wd2qK//amx/c+A8O2fVeZA==, - } + '@firebase/auth-types@0.12.2': + resolution: {integrity: sha512-qsEBaRMoGvHO10unlDJhaKSuPn4pyoTtlQuP1ghZfzB6rNQPuhp/N/DcFZxm9i4v0SogjCbf9reWupwIvfmH6w==} peerDependencies: '@firebase/app-types': 0.x '@firebase/util': 1.x - dependencies: - '@firebase/app-types': 0.9.0 - '@firebase/util': 1.9.3 - dev: false - /@firebase/auth@0.23.2(@firebase/app@0.9.13): - resolution: - { - integrity: sha512-dM9iJ0R6tI1JczuGSxXmQbXAgtYie0K4WvKcuyuSTCu9V8eEDiz4tfa1sO3txsfvwg7nOY3AjoCyMYEdqZ8hdg==, - } + '@firebase/auth@1.7.9': + resolution: {integrity: sha512-yLD5095kVgDw965jepMyUrIgDklD6qH/BZNHeKOgvu7pchOKNjVM+zQoOVYJIKWMWOWBq8IRNVU6NXzBbozaJg==} peerDependencies: '@firebase/app': 0.x - dependencies: - '@firebase/app': 0.9.13 - '@firebase/component': 0.6.4 - '@firebase/logger': 0.4.0 - '@firebase/util': 1.9.3 - node-fetch: 2.6.7 - tslib: 2.6.2 - transitivePeerDependencies: - - encoding - dev: false + '@react-native-async-storage/async-storage': ^1.18.1 + peerDependenciesMeta: + '@react-native-async-storage/async-storage': + optional: true - /@firebase/component@0.5.21: - resolution: - { - integrity: sha512-12MMQ/ulfygKpEJpseYMR0HunJdlsLrwx2XcEs40M18jocy2+spyzHHEwegN3x/2/BLFBjR5247Etmz0G97Qpg==, - } - requiresBuild: true - dependencies: - '@firebase/util': 1.7.3 - tslib: 2.6.2 - dev: false - optional: true + '@firebase/component@0.5.21': + resolution: {integrity: sha512-12MMQ/ulfygKpEJpseYMR0HunJdlsLrwx2XcEs40M18jocy2+spyzHHEwegN3x/2/BLFBjR5247Etmz0G97Qpg==} - /@firebase/component@0.6.4: - resolution: - { - integrity: sha512-rLMyrXuO9jcAUCaQXCMjCMUsWrba5fzHlNK24xz5j2W6A/SRmK8mZJ/hn7V0fViLbxC0lPMtrK1eYzk6Fg03jA==, - } - dependencies: - '@firebase/util': 1.9.3 - tslib: 2.6.2 - dev: false + '@firebase/component@0.6.9': + resolution: {integrity: sha512-gm8EUEJE/fEac86AvHn8Z/QW8BvR56TBw3hMW0O838J/1mThYQXAIQBgUv75EqlCZfdawpWLrKt1uXvp9ciK3Q==} - /@firebase/database-compat@0.2.10(@firebase/app-types@0.9.0): - resolution: - { - integrity: sha512-fK+IgUUqVKcWK/gltzDU+B1xauCOfY6vulO8lxoNTkcCGlSxuTtwsdqjGkFmgFRMYjXFWWJ6iFcJ/vXahzwCtA==, - } - requiresBuild: true - dependencies: - '@firebase/component': 0.5.21 - '@firebase/database': 0.13.10(@firebase/app-types@0.9.0) - '@firebase/database-types': 0.9.17 - '@firebase/logger': 0.3.4 - '@firebase/util': 1.7.3 - tslib: 2.6.2 - transitivePeerDependencies: - - '@firebase/app-types' - dev: false - optional: true + '@firebase/data-connect@0.1.0': + resolution: {integrity: sha512-vSe5s8dY13ilhLnfY0eYRmQsdTbH7PUFZtBbqU6JVX/j8Qp9A6G5gG6//ulbX9/1JFOF1IWNOne9c8S/DOCJaQ==} + peerDependencies: + '@firebase/app': 0.x - /@firebase/database-compat@0.3.4: - resolution: - { - integrity: sha512-kuAW+l+sLMUKBThnvxvUZ+Q1ZrF/vFJ58iUY9kAcbX48U03nVzIF6Tmkf0p3WVQwMqiXguSgtOPIB6ZCeF+5Gg==, - } - dependencies: - '@firebase/component': 0.6.4 - '@firebase/database': 0.14.4 - '@firebase/database-types': 0.10.4 - '@firebase/logger': 0.4.0 - '@firebase/util': 1.9.3 - tslib: 2.6.2 - dev: false + '@firebase/database-compat@0.2.10': + resolution: {integrity: sha512-fK+IgUUqVKcWK/gltzDU+B1xauCOfY6vulO8lxoNTkcCGlSxuTtwsdqjGkFmgFRMYjXFWWJ6iFcJ/vXahzwCtA==} - /@firebase/database-types@0.10.4: - resolution: - { - integrity: sha512-dPySn0vJ/89ZeBac70T+2tWWPiJXWbmRygYv0smT5TfE3hDrQ09eKMF3Y+vMlTdrMWq7mUdYW5REWPSGH4kAZQ==, - } - dependencies: - '@firebase/app-types': 0.9.0 - '@firebase/util': 1.9.3 - dev: false + '@firebase/database-compat@1.0.8': + resolution: {integrity: sha512-OpeWZoPE3sGIRPBKYnW9wLad25RaWbGyk7fFQe4xnJQKRzlynWeFBSRRAoLE2Old01WXwskUiucNqUUVlFsceg==} - /@firebase/database-types@0.9.17: - resolution: - { - integrity: sha512-YQm2tCZyxNtEnlS5qo5gd2PAYgKCy69tUKwioGhApCFThW+mIgZs7IeYeJo2M51i4LCixYUl+CvnOyAnb/c3XA==, - } - requiresBuild: true - dependencies: - '@firebase/app-types': 0.8.1 - '@firebase/util': 1.7.3 - dev: false - optional: true + '@firebase/database-types@0.9.17': + resolution: {integrity: sha512-YQm2tCZyxNtEnlS5qo5gd2PAYgKCy69tUKwioGhApCFThW+mIgZs7IeYeJo2M51i4LCixYUl+CvnOyAnb/c3XA==} - /@firebase/database@0.13.10(@firebase/app-types@0.9.0): - resolution: - { - integrity: sha512-KRucuzZ7ZHQsRdGEmhxId5jyM2yKsjsQWF9yv0dIhlxYg0D8rCVDZc/waoPKA5oV3/SEIoptF8F7R1Vfe7BCQA==, - } - requiresBuild: true - dependencies: - '@firebase/auth-interop-types': 0.1.7(@firebase/app-types@0.9.0)(@firebase/util@1.7.3) - '@firebase/component': 0.5.21 - '@firebase/logger': 0.3.4 - '@firebase/util': 1.7.3 - faye-websocket: 0.11.4 - tslib: 2.6.2 - transitivePeerDependencies: - - '@firebase/app-types' - dev: false - optional: true + '@firebase/database-types@1.0.5': + resolution: {integrity: sha512-fTlqCNwFYyq/C6W7AJ5OCuq5CeZuBEsEwptnVxlNPkWCo5cTTyukzAHRSO/jaQcItz33FfYrrFk1SJofcu2AaQ==} - /@firebase/database@0.14.4: - resolution: - { - integrity: sha512-+Ea/IKGwh42jwdjCyzTmeZeLM3oy1h0mFPsTy6OqCWzcu/KFqRAr5Tt1HRCOBlNOdbh84JPZC47WLU18n2VbxQ==, - } - dependencies: - '@firebase/auth-interop-types': 0.2.1 - '@firebase/component': 0.6.4 - '@firebase/logger': 0.4.0 - '@firebase/util': 1.9.3 - faye-websocket: 0.11.4 - tslib: 2.6.2 - dev: false + '@firebase/database@0.13.10': + resolution: {integrity: sha512-KRucuzZ7ZHQsRdGEmhxId5jyM2yKsjsQWF9yv0dIhlxYg0D8rCVDZc/waoPKA5oV3/SEIoptF8F7R1Vfe7BCQA==} - /@firebase/firestore-compat@0.3.12(@firebase/app-compat@0.2.13)(@firebase/app-types@0.9.0)(@firebase/app@0.9.13): - resolution: - { - integrity: sha512-mazuNGAx5Kt9Nph0pm6ULJFp/+j7GSsx+Ncw1GrnKl+ft1CQ4q2LcUssXnjqkX2Ry0fNGqUzC1mfIUrk9bYtjQ==, - } + '@firebase/database@1.0.8': + resolution: {integrity: sha512-dzXALZeBI1U5TXt6619cv0+tgEhJiwlUtQ55WNZY7vGAjv7Q1QioV969iYwt1AQQ0ovHnEW0YW9TiBfefLvErg==} + + '@firebase/firestore-compat@0.3.38': + resolution: {integrity: sha512-GoS0bIMMkjpLni6StSwRJarpu2+S5m346Na7gr9YZ/BZ/W3/8iHGNr9PxC+f0rNZXqS4fGRn88pICjrZEgbkqQ==} peerDependencies: '@firebase/app-compat': 0.x - dependencies: - '@firebase/app-compat': 0.2.13 - '@firebase/component': 0.6.4 - '@firebase/firestore': 3.13.0(@firebase/app@0.9.13) - '@firebase/firestore-types': 2.5.1(@firebase/app-types@0.9.0)(@firebase/util@1.9.3) - '@firebase/util': 1.9.3 - tslib: 2.6.2 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/app-types' - - encoding - dev: false - /@firebase/firestore-types@2.5.1(@firebase/app-types@0.9.0)(@firebase/util@1.9.3): - resolution: - { - integrity: sha512-xG0CA6EMfYo8YeUxC8FeDzf6W3FX1cLlcAGBYV6Cku12sZRI81oWcu61RSKM66K6kUENP+78Qm8mvroBcm1whw==, - } + '@firebase/firestore-types@3.0.2': + resolution: {integrity: sha512-wp1A+t5rI2Qc/2q7r2ZpjUXkRVPtGMd6zCLsiWurjsQpqPgFin3AhNibKcIzoF2rnToNa/XYtyWXuifjOOwDgg==} peerDependencies: '@firebase/app-types': 0.x '@firebase/util': 1.x - dependencies: - '@firebase/app-types': 0.9.0 - '@firebase/util': 1.9.3 - dev: false - /@firebase/firestore@3.13.0(@firebase/app@0.9.13): - resolution: - { - integrity: sha512-NwcnU+madJXQ4fbLkGx1bWvL612IJN/qO6bZ6dlPmyf7QRyu5azUosijdAN675r+bOOJxMtP1Bv981bHBXAbUg==, - } - engines: { node: '>=10.10.0' } + '@firebase/firestore@4.7.3': + resolution: {integrity: sha512-NwVU+JPZ/3bhvNSJMCSzfcBZZg8SUGyzZ2T0EW3/bkUeefCyzMISSt/TTIfEHc8cdyXGlMqfGe3/62u9s74UEg==} + engines: {node: '>=10.10.0'} peerDependencies: '@firebase/app': 0.x - dependencies: - '@firebase/app': 0.9.13 - '@firebase/component': 0.6.4 - '@firebase/logger': 0.4.0 - '@firebase/util': 1.9.3 - '@firebase/webchannel-wrapper': 0.10.1 - '@grpc/grpc-js': 1.7.3 - '@grpc/proto-loader': 0.6.13 - node-fetch: 2.6.7 - tslib: 2.6.2 - transitivePeerDependencies: - - encoding - dev: false - /@firebase/functions-compat@0.3.5(@firebase/app-compat@0.2.13)(@firebase/app@0.9.13): - resolution: - { - integrity: sha512-uD4jwgwVqdWf6uc3NRKF8cSZ0JwGqSlyhPgackyUPe+GAtnERpS4+Vr66g0b3Gge0ezG4iyHo/EXW/Hjx7QhHw==, - } + '@firebase/functions-compat@0.3.14': + resolution: {integrity: sha512-dZ0PKOKQFnOlMfcim39XzaXonSuPPAVuzpqA4ONTIdyaJK/OnBaIEVs/+BH4faa1a2tLeR+Jy15PKqDRQoNIJw==} peerDependencies: '@firebase/app-compat': 0.x - dependencies: - '@firebase/app-compat': 0.2.13 - '@firebase/component': 0.6.4 - '@firebase/functions': 0.10.0(@firebase/app@0.9.13) - '@firebase/functions-types': 0.6.0 - '@firebase/util': 1.9.3 - tslib: 2.6.2 - transitivePeerDependencies: - - '@firebase/app' - - encoding - dev: false - /@firebase/functions-types@0.6.0: - resolution: - { - integrity: sha512-hfEw5VJtgWXIRf92ImLkgENqpL6IWpYaXVYiRkFY1jJ9+6tIhWM7IzzwbevwIIud/jaxKVdRzD7QBWfPmkwCYw==, - } - dev: false + '@firebase/functions-types@0.6.2': + resolution: {integrity: sha512-0KiJ9lZ28nS2iJJvimpY4nNccV21rkQyor5Iheu/nq8aKXJqtJdeSlZDspjPSBBiHRzo7/GMUttegnsEITqR+w==} - /@firebase/functions@0.10.0(@firebase/app@0.9.13): - resolution: - { - integrity: sha512-2U+fMNxTYhtwSpkkR6WbBcuNMOVaI7MaH3cZ6UAeNfj7AgEwHwMIFLPpC13YNZhno219F0lfxzTAA0N62ndWzA==, - } + '@firebase/functions@0.11.8': + resolution: {integrity: sha512-Lo2rTPDn96naFIlSZKVd1yvRRqqqwiJk7cf9TZhUerwnPKgBzXy+aHE22ry+6EjCaQusUoNai6mU6p+G8QZT1g==} peerDependencies: '@firebase/app': 0.x - dependencies: - '@firebase/app': 0.9.13 - '@firebase/app-check-interop-types': 0.3.0 - '@firebase/auth-interop-types': 0.2.1 - '@firebase/component': 0.6.4 - '@firebase/messaging-interop-types': 0.2.0 - '@firebase/util': 1.9.3 - node-fetch: 2.6.7 - tslib: 2.6.2 - transitivePeerDependencies: - - encoding - dev: false - /@firebase/installations-compat@0.2.4(@firebase/app-compat@0.2.13)(@firebase/app-types@0.9.0)(@firebase/app@0.9.13): - resolution: - { - integrity: sha512-LI9dYjp0aT9Njkn9U4JRrDqQ6KXeAmFbRC0E7jI7+hxl5YmRWysq5qgQl22hcWpTk+cm3es66d/apoDU/A9n6Q==, - } + '@firebase/installations-compat@0.2.9': + resolution: {integrity: sha512-2lfdc6kPXR7WaL4FCQSQUhXcPbI7ol3wF+vkgtU25r77OxPf8F/VmswQ7sgIkBBWtymn5ZF20TIKtnOj9rjb6w==} peerDependencies: '@firebase/app-compat': 0.x - dependencies: - '@firebase/app-compat': 0.2.13 - '@firebase/component': 0.6.4 - '@firebase/installations': 0.6.4(@firebase/app@0.9.13) - '@firebase/installations-types': 0.5.0(@firebase/app-types@0.9.0) - '@firebase/util': 1.9.3 - tslib: 2.6.2 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/app-types' - dev: false - /@firebase/installations-types@0.5.0(@firebase/app-types@0.9.0): - resolution: - { - integrity: sha512-9DP+RGfzoI2jH7gY4SlzqvZ+hr7gYzPODrbzVD82Y12kScZ6ZpRg/i3j6rleto8vTFC8n6Len4560FnV1w2IRg==, - } + '@firebase/installations-types@0.5.2': + resolution: {integrity: sha512-que84TqGRZJpJKHBlF2pkvc1YcXrtEDOVGiDjovP/a3s6W4nlbohGXEsBJo0JCeeg/UG9A+DEZVDUV9GpklUzA==} peerDependencies: '@firebase/app-types': 0.x - dependencies: - '@firebase/app-types': 0.9.0 - dev: false - /@firebase/installations@0.6.4(@firebase/app@0.9.13): - resolution: - { - integrity: sha512-u5y88rtsp7NYkCHC3ElbFBrPtieUybZluXyzl7+4BsIz4sqb4vSAuwHEUgCgCeaQhvsnxDEU6icly8U9zsJigA==, - } + '@firebase/installations@0.6.9': + resolution: {integrity: sha512-hlT7AwCiKghOX3XizLxXOsTFiFCQnp/oj86zp1UxwDGmyzsyoxtX+UIZyVyH/oBF5+XtblFG9KZzZQ/h+dpy+Q==} peerDependencies: '@firebase/app': 0.x - dependencies: - '@firebase/app': 0.9.13 - '@firebase/component': 0.6.4 - '@firebase/util': 1.9.3 - idb: 7.0.1 - tslib: 2.6.2 - dev: false - /@firebase/logger@0.3.4: - resolution: - { - integrity: sha512-hlFglGRgZEwoyClZcGLx/Wd+zoLfGmbDkFx56mQt/jJ0XMbfPqwId1kiPl0zgdWZX+D8iH+gT6GuLPFsJWgiGw==, - } - requiresBuild: true - dependencies: - tslib: 2.6.2 - dev: false - optional: true + '@firebase/logger@0.3.4': + resolution: {integrity: sha512-hlFglGRgZEwoyClZcGLx/Wd+zoLfGmbDkFx56mQt/jJ0XMbfPqwId1kiPl0zgdWZX+D8iH+gT6GuLPFsJWgiGw==} - /@firebase/logger@0.4.0: - resolution: - { - integrity: sha512-eRKSeykumZ5+cJPdxxJRgAC3G5NknY2GwEbKfymdnXtnT0Ucm4pspfR6GT4MUQEDuJwRVbVcSx85kgJulMoFFA==, - } - dependencies: - tslib: 2.6.2 - dev: false + '@firebase/logger@0.4.2': + resolution: {integrity: sha512-Q1VuA5M1Gjqrwom6I6NUU4lQXdo9IAQieXlujeHZWvRt1b7qQ0KwBaNAjgxG27jgF9/mUwsNmO8ptBCGVYhB0A==} - /@firebase/messaging-compat@0.2.4(@firebase/app-compat@0.2.13)(@firebase/app@0.9.13): - resolution: - { - integrity: sha512-lyFjeUhIsPRYDPNIkYX1LcZMpoVbBWXX4rPl7c/rqc7G+EUea7IEtSt4MxTvh6fDfPuzLn7+FZADfscC+tNMfg==, - } + '@firebase/messaging-compat@0.2.12': + resolution: {integrity: sha512-pKsiUVZrbmRgdImYqhBNZlkKJbqjlPkVdQRZGRbkTyX4OSGKR0F/oJeCt1a8jEg5UnBp4fdVwSWSp4DuCovvEQ==} peerDependencies: '@firebase/app-compat': 0.x - dependencies: - '@firebase/app-compat': 0.2.13 - '@firebase/component': 0.6.4 - '@firebase/messaging': 0.12.4(@firebase/app@0.9.13) - '@firebase/util': 1.9.3 - tslib: 2.6.2 - transitivePeerDependencies: - - '@firebase/app' - dev: false - /@firebase/messaging-interop-types@0.2.0: - resolution: - { - integrity: sha512-ujA8dcRuVeBixGR9CtegfpU4YmZf3Lt7QYkcj693FFannwNuZgfAYaTmbJ40dtjB81SAu6tbFPL9YLNT15KmOQ==, - } - dev: false + '@firebase/messaging-interop-types@0.2.2': + resolution: {integrity: sha512-l68HXbuD2PPzDUOFb3aG+nZj5KA3INcPwlocwLZOzPp9rFM9yeuI9YLl6DQfguTX5eAGxO0doTR+rDLDvQb5tA==} - /@firebase/messaging@0.12.4(@firebase/app@0.9.13): - resolution: - { - integrity: sha512-6JLZct6zUaex4g7HI3QbzeUrg9xcnmDAPTWpkoMpd/GoSVWH98zDoWXMGrcvHeCAIsLpFMe4MPoZkJbrPhaASw==, - } + '@firebase/messaging@0.12.12': + resolution: {integrity: sha512-6q0pbzYBJhZEtUoQx7hnPhZvAbuMNuBXKQXOx2YlWhSrlv9N1m0ZzlNpBbu/ItTzrwNKTibdYzUyaaxdWLg+4w==} peerDependencies: '@firebase/app': 0.x - dependencies: - '@firebase/app': 0.9.13 - '@firebase/component': 0.6.4 - '@firebase/installations': 0.6.4(@firebase/app@0.9.13) - '@firebase/messaging-interop-types': 0.2.0 - '@firebase/util': 1.9.3 - idb: 7.0.1 - tslib: 2.6.2 - dev: false - /@firebase/performance-compat@0.2.4(@firebase/app-compat@0.2.13)(@firebase/app@0.9.13): - resolution: - { - integrity: sha512-nnHUb8uP9G8islzcld/k6Bg5RhX62VpbAb/Anj7IXs/hp32Eb2LqFPZK4sy3pKkBUO5wcrlRWQa6wKOxqlUqsg==, - } + '@firebase/performance-compat@0.2.9': + resolution: {integrity: sha512-dNl95IUnpsu3fAfYBZDCVhXNkASE0uo4HYaEPd2/PKscfTvsgqFAOxfAXzBEDOnynDWiaGUnb5M1O00JQ+3FXA==} peerDependencies: '@firebase/app-compat': 0.x - dependencies: - '@firebase/app-compat': 0.2.13 - '@firebase/component': 0.6.4 - '@firebase/logger': 0.4.0 - '@firebase/performance': 0.6.4(@firebase/app@0.9.13) - '@firebase/performance-types': 0.2.0 - '@firebase/util': 1.9.3 - tslib: 2.6.2 - transitivePeerDependencies: - - '@firebase/app' - dev: false - /@firebase/performance-types@0.2.0: - resolution: - { - integrity: sha512-kYrbr8e/CYr1KLrLYZZt2noNnf+pRwDq2KK9Au9jHrBMnb0/C9X9yWSXmZkFt4UIdsQknBq8uBB7fsybZdOBTA==, - } - dev: false + '@firebase/performance-types@0.2.2': + resolution: {integrity: sha512-gVq0/lAClVH5STrIdKnHnCo2UcPLjJlDUoEB/tB4KM+hAeHUxWKnpT0nemUPvxZ5nbdY/pybeyMe8Cs29gEcHA==} - /@firebase/performance@0.6.4(@firebase/app@0.9.13): - resolution: - { - integrity: sha512-HfTn/bd8mfy/61vEqaBelNiNnvAbUtME2S25A67Nb34zVuCSCRIX4SseXY6zBnOFj3oLisaEqhVcJmVPAej67g==, - } + '@firebase/performance@0.6.9': + resolution: {integrity: sha512-PnVaak5sqfz5ivhua+HserxTJHtCar/7zM0flCX6NkzBNzJzyzlH4Hs94h2Il0LQB99roBqoE5QT1JqWqcLJHQ==} peerDependencies: '@firebase/app': 0.x - dependencies: - '@firebase/app': 0.9.13 - '@firebase/component': 0.6.4 - '@firebase/installations': 0.6.4(@firebase/app@0.9.13) - '@firebase/logger': 0.4.0 - '@firebase/util': 1.9.3 - tslib: 2.6.2 - dev: false - /@firebase/remote-config-compat@0.2.4(@firebase/app-compat@0.2.13)(@firebase/app@0.9.13): - resolution: - { - integrity: sha512-FKiki53jZirrDFkBHglB3C07j5wBpitAaj8kLME6g8Mx+aq7u9P7qfmuSRytiOItADhWUj7O1JIv7n9q87SuwA==, - } + '@firebase/remote-config-compat@0.2.9': + resolution: {integrity: sha512-AxzGpWfWFYejH2twxfdOJt5Cfh/ATHONegTd/a0p5flEzsD5JsxXgfkFToop+mypEL3gNwawxrxlZddmDoNxyA==} peerDependencies: '@firebase/app-compat': 0.x - dependencies: - '@firebase/app-compat': 0.2.13 - '@firebase/component': 0.6.4 - '@firebase/logger': 0.4.0 - '@firebase/remote-config': 0.4.4(@firebase/app@0.9.13) - '@firebase/remote-config-types': 0.3.0 - '@firebase/util': 1.9.3 - tslib: 2.6.2 - transitivePeerDependencies: - - '@firebase/app' - dev: false - /@firebase/remote-config-types@0.3.0: - resolution: - { - integrity: sha512-RtEH4vdcbXZuZWRZbIRmQVBNsE7VDQpet2qFvq6vwKLBIQRQR5Kh58M4ok3A3US8Sr3rubYnaGqZSurCwI8uMA==, - } - dev: false + '@firebase/remote-config-types@0.3.2': + resolution: {integrity: sha512-0BC4+Ud7y2aPTyhXJTMTFfrGGLqdYXrUB9sJVAB8NiqJswDTc4/2qrE/yfUbnQJhbSi6ZaTTBKyG3n1nplssaA==} - /@firebase/remote-config@0.4.4(@firebase/app@0.9.13): - resolution: - { - integrity: sha512-x1ioTHGX8ZwDSTOVp8PBLv2/wfwKzb4pxi0gFezS5GCJwbLlloUH4YYZHHS83IPxnua8b6l0IXUaWd0RgbWwzQ==, - } + '@firebase/remote-config@0.4.9': + resolution: {integrity: sha512-EO1NLCWSPMHdDSRGwZ73kxEEcTopAxX1naqLJFNApp4hO8WfKfmEpmjxmP5TrrnypjIf2tUkYaKsfbEA7+AMmA==} peerDependencies: '@firebase/app': 0.x - dependencies: - '@firebase/app': 0.9.13 - '@firebase/component': 0.6.4 - '@firebase/installations': 0.6.4(@firebase/app@0.9.13) - '@firebase/logger': 0.4.0 - '@firebase/util': 1.9.3 - tslib: 2.6.2 - dev: false - /@firebase/storage-compat@0.3.2(@firebase/app-compat@0.2.13)(@firebase/app-types@0.9.0)(@firebase/app@0.9.13): - resolution: - { - integrity: sha512-wvsXlLa9DVOMQJckbDNhXKKxRNNewyUhhbXev3t8kSgoCotd1v3MmqhKKz93ePhDnhHnDs7bYHy+Qa8dRY6BXw==, - } + '@firebase/storage-compat@0.3.12': + resolution: {integrity: sha512-hA4VWKyGU5bWOll+uwzzhEMMYGu9PlKQc1w4DWxB3aIErWYzonrZjF0icqNQZbwKNIdh8SHjZlFeB2w6OSsjfg==} peerDependencies: '@firebase/app-compat': 0.x - dependencies: - '@firebase/app-compat': 0.2.13 - '@firebase/component': 0.6.4 - '@firebase/storage': 0.11.2(@firebase/app@0.9.13) - '@firebase/storage-types': 0.8.0(@firebase/app-types@0.9.0)(@firebase/util@1.9.3) - '@firebase/util': 1.9.3 - tslib: 2.6.2 - transitivePeerDependencies: - - '@firebase/app' - - '@firebase/app-types' - - encoding - dev: false - /@firebase/storage-types@0.8.0(@firebase/app-types@0.9.0)(@firebase/util@1.9.3): - resolution: - { - integrity: sha512-isRHcGrTs9kITJC0AVehHfpraWFui39MPaU7Eo8QfWlqW7YPymBmRgjDrlOgFdURh6Cdeg07zmkLP5tzTKRSpg==, - } + '@firebase/storage-types@0.8.2': + resolution: {integrity: sha512-0vWu99rdey0g53lA7IShoA2Lol1jfnPovzLDUBuon65K7uKG9G+L5uO05brD9pMw+l4HRFw23ah3GwTGpEav6g==} peerDependencies: '@firebase/app-types': 0.x '@firebase/util': 1.x - dependencies: - '@firebase/app-types': 0.9.0 - '@firebase/util': 1.9.3 - dev: false - /@firebase/storage@0.11.2(@firebase/app@0.9.13): - resolution: - { - integrity: sha512-CtvoFaBI4hGXlXbaCHf8humajkbXhs39Nbh6MbNxtwJiCqxPy9iH3D3CCfXAvP0QvAAwmJUTK3+z9a++Kc4nkA==, - } + '@firebase/storage@0.13.2': + resolution: {integrity: sha512-fxuJnHshbhVwuJ4FuISLu+/76Aby2sh+44ztjF2ppoe0TELIDxPW6/r1KGlWYt//AD0IodDYYA8ZTN89q8YqUw==} peerDependencies: '@firebase/app': 0.x - dependencies: - '@firebase/app': 0.9.13 - '@firebase/component': 0.6.4 - '@firebase/util': 1.9.3 - node-fetch: 2.6.7 - tslib: 2.6.2 - transitivePeerDependencies: - - encoding - dev: false - /@firebase/util@1.7.3: - resolution: - { - integrity: sha512-wxNqWbqokF551WrJ9BIFouU/V5SL1oYCGx1oudcirdhadnQRFH5v1sjgGL7cUV/UsekSycygphdrF2lxBxOYKg==, - } - requiresBuild: true - dependencies: - tslib: 2.6.2 - dev: false - optional: true + '@firebase/util@1.10.0': + resolution: {integrity: sha512-xKtx4A668icQqoANRxyDLBLz51TAbDP9KRfpbKGxiCAW346d0BeJe5vN6/hKxxmWwnZ0mautyv39JxviwwQMOQ==} - /@firebase/util@1.9.3: - resolution: - { - integrity: sha512-DY02CRhOZwpzO36fHpuVysz6JZrscPiBXD0fXp6qSrL9oNOx5KWICKdR95C0lSITzxp0TZosVyHqzatE8JbcjA==, - } - dependencies: - tslib: 2.6.2 - dev: false - - /@firebase/webchannel-wrapper@0.10.1: - resolution: - { - integrity: sha512-Dq5rYfEpdeel0bLVN+nfD1VWmzCkK+pJbSjIawGE+RY4+NIJqhbUDDQjvV0NUK84fMfwxvtFoCtEe70HfZjFcw==, - } - dev: false - - /@gar/promisify@1.1.3: - resolution: - { - integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==, - } - dev: false - - /@google-cloud/firestore@4.15.1: - resolution: - { - integrity: sha512-2PWsCkEF1W02QbghSeRsNdYKN1qavrHBP3m72gPDMHQSYrGULOaTi7fSJquQmAtc4iPVB2/x6h80rdLHTATQtA==, - } - engines: { node: '>=10.10.0' } - requiresBuild: true - dependencies: - fast-deep-equal: 3.1.3 - functional-red-black-tree: 1.0.1 - google-gax: 2.30.5 - protobufjs: 6.11.4 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - optional: true + '@firebase/util@1.7.3': + resolution: {integrity: sha512-wxNqWbqokF551WrJ9BIFouU/V5SL1oYCGx1oudcirdhadnQRFH5v1sjgGL7cUV/UsekSycygphdrF2lxBxOYKg==} - /@google-cloud/paginator@3.0.7: - resolution: - { - integrity: sha512-jJNutk0arIQhmpUUQJPJErsojqo834KcyB6X7a1mxuic8i1tKXxde8E69IZxNZawRIlZdIK2QY4WALvlK5MzYQ==, - } - engines: { node: '>=10' } - requiresBuild: true - dependencies: - arrify: 2.0.1 - extend: 3.0.2 - dev: false - optional: true + '@firebase/vertexai-preview@0.0.4': + resolution: {integrity: sha512-EBSqyu9eg8frQlVU9/HjKtHN7odqbh9MtAcVz3WwHj4gLCLOoN9F/o+oxlq3CxvFrd3CNTZwu6d2mZtVlEInng==} + engines: {node: '>=18.0.0'} + peerDependencies: + '@firebase/app': 0.x + '@firebase/app-types': 0.x - /@google-cloud/projectify@2.1.1: - resolution: - { - integrity: sha512-+rssMZHnlh0twl122gXY4/aCrk0G1acBqkHFfYddtsqpYXGxA29nj9V5V9SfC+GyOG00l650f6lG9KL+EpFEWQ==, - } - engines: { node: '>=10' } - requiresBuild: true - dev: false - optional: true + '@firebase/webchannel-wrapper@1.0.1': + resolution: {integrity: sha512-jmEnr/pk0yVkA7mIlHNnxCi+wWzOFUg0WyIotgkKAb2u1J7fAeDBcVNSTjTihbAYNusCLQdW5s9IJ5qwnEufcQ==} - /@google-cloud/promisify@2.0.4: - resolution: - { - integrity: sha512-j8yRSSqswWi1QqUGKVEKOG03Q7qOoZP6/h2zN2YO+F5h2+DHU0bSrHCK9Y7lo2DI9fBd8qGAw795sf+3Jva4yA==, - } - engines: { node: '>=10' } - requiresBuild: true - dev: false - optional: true + '@gar/promisify@1.1.3': + resolution: {integrity: sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==} - /@google-cloud/storage@5.20.5: - resolution: - { - integrity: sha512-lOs/dCyveVF8TkVFnFSF7IGd0CJrTm91qiK6JLu+Z8qiT+7Ag0RyVhxZIWkhiACqwABo7kSHDm8FdH8p2wxSSw==, - } - engines: { node: '>=10' } - requiresBuild: true - dependencies: - '@google-cloud/paginator': 3.0.7 - '@google-cloud/projectify': 2.1.1 - '@google-cloud/promisify': 2.0.4 - abort-controller: 3.0.0 - arrify: 2.0.1 - async-retry: 1.3.3 - compressible: 2.0.18 - configstore: 5.0.1 - duplexify: 4.1.2 - ent: 2.2.0 - extend: 3.0.2 - gaxios: 4.3.3 - google-auth-library: 7.14.1 - hash-stream-validation: 0.2.4 - mime: 3.0.0 - mime-types: 2.1.35 - p-limit: 3.1.0 - pumpify: 2.0.1 - retry-request: 4.2.2 - stream-events: 1.0.5 - teeny-request: 7.2.0 - uuid: 8.3.2 - xdg-basedir: 4.0.0 - transitivePeerDependencies: - - encoding - - supports-color - dev: false - optional: true + '@google-cloud/firestore@4.15.1': + resolution: {integrity: sha512-2PWsCkEF1W02QbghSeRsNdYKN1qavrHBP3m72gPDMHQSYrGULOaTi7fSJquQmAtc4iPVB2/x6h80rdLHTATQtA==} + engines: {node: '>=10.10.0'} - /@grpc/grpc-js@1.6.12: - resolution: - { - integrity: sha512-JmvQ03OTSpVd9JTlj/K3IWHSz4Gk/JMLUTtW7Zb0KvO1LcOYGATh5cNuRYzCAeDR3O8wq+q8FZe97eO9MBrkUw==, - } - engines: { node: ^8.13.0 || >=10.10.0 } - requiresBuild: true - dependencies: - '@grpc/proto-loader': 0.7.10 - '@types/node': 20.8.0 - dev: false - optional: true + '@google-cloud/paginator@3.0.7': + resolution: {integrity: sha512-jJNutk0arIQhmpUUQJPJErsojqo834KcyB6X7a1mxuic8i1tKXxde8E69IZxNZawRIlZdIK2QY4WALvlK5MzYQ==} + engines: {node: '>=10'} - /@grpc/grpc-js@1.7.3: - resolution: - { - integrity: sha512-H9l79u4kJ2PVSxUNA08HMYAnUBLj9v6KjYQ7SQ71hOZcEXhShE/y5iQCesP8+6/Ik/7i2O0a10bPquIcYfufog==, - } - engines: { node: ^8.13.0 || >=10.10.0 } - dependencies: - '@grpc/proto-loader': 0.7.10 - '@types/node': 20.8.0 - dev: false - - /@grpc/proto-loader@0.6.13: - resolution: - { - integrity: sha512-FjxPYDRTn6Ec3V0arm1FtSpmP6V50wuph2yILpyvTKzjc76oDdoihXqM1DzOW5ubvCC8GivfCnNtfaRE8myJ7g==, - } - engines: { node: '>=6' } + '@google-cloud/projectify@2.1.1': + resolution: {integrity: sha512-+rssMZHnlh0twl122gXY4/aCrk0G1acBqkHFfYddtsqpYXGxA29nj9V5V9SfC+GyOG00l650f6lG9KL+EpFEWQ==} + engines: {node: '>=10'} + + '@google-cloud/promisify@2.0.4': + resolution: {integrity: sha512-j8yRSSqswWi1QqUGKVEKOG03Q7qOoZP6/h2zN2YO+F5h2+DHU0bSrHCK9Y7lo2DI9fBd8qGAw795sf+3Jva4yA==} + engines: {node: '>=10'} + + '@google-cloud/storage@5.20.5': + resolution: {integrity: sha512-lOs/dCyveVF8TkVFnFSF7IGd0CJrTm91qiK6JLu+Z8qiT+7Ag0RyVhxZIWkhiACqwABo7kSHDm8FdH8p2wxSSw==} + engines: {node: '>=10'} + + '@grpc/grpc-js@1.6.12': + resolution: {integrity: sha512-JmvQ03OTSpVd9JTlj/K3IWHSz4Gk/JMLUTtW7Zb0KvO1LcOYGATh5cNuRYzCAeDR3O8wq+q8FZe97eO9MBrkUw==} + engines: {node: ^8.13.0 || >=10.10.0} + + '@grpc/grpc-js@1.9.15': + resolution: {integrity: sha512-nqE7Hc0AzI+euzUwDAy0aY5hCp10r734gMGRdU+qOPX0XSceI2ULrcXB5U2xSc5VkWwalCj4M7GzCAygZl2KoQ==} + engines: {node: ^8.13.0 || >=10.10.0} + + '@grpc/proto-loader@0.6.13': + resolution: {integrity: sha512-FjxPYDRTn6Ec3V0arm1FtSpmP6V50wuph2yILpyvTKzjc76oDdoihXqM1DzOW5ubvCC8GivfCnNtfaRE8myJ7g==} + engines: {node: '>=6'} hasBin: true - dependencies: - '@types/long': 4.0.2 - lodash.camelcase: 4.3.0 - long: 4.0.0 - protobufjs: 6.11.4 - yargs: 16.2.0 - dev: false - - /@grpc/proto-loader@0.7.10: - resolution: - { - integrity: sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ==, - } - engines: { node: '>=6' } + + '@grpc/proto-loader@0.7.10': + resolution: {integrity: sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ==} + engines: {node: '>=6'} hasBin: true - dependencies: - lodash.camelcase: 4.3.0 - long: 5.2.3 - protobufjs: 7.2.5 - yargs: 17.7.2 - dev: false - /@humanwhocodes/config-array@0.11.13: - resolution: - { - integrity: sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==, - } - engines: { node: '>=10.10.0' } - dependencies: - '@humanwhocodes/object-schema': 2.0.1 - debug: 4.3.4 - minimatch: 3.1.2 - transitivePeerDependencies: - - supports-color - dev: true - - /@humanwhocodes/module-importer@1.0.1: - resolution: - { - integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==, - } - engines: { node: '>=12.22' } - dev: true - - /@humanwhocodes/object-schema@2.0.1: - resolution: - { - integrity: sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==, - } - dev: true - - /@istanbuljs/load-nyc-config@1.1.0: - resolution: - { - integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==, - } - engines: { node: '>=8' } - dependencies: - camelcase: 5.3.1 - find-up: 4.1.0 - get-package-type: 0.1.0 - js-yaml: 3.14.1 - resolve-from: 5.0.0 - dev: true - - /@istanbuljs/schema@0.1.3: - resolution: - { - integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==, - } - engines: { node: '>=8' } - dev: true - - /@jest/console@27.5.1: - resolution: - { - integrity: sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - dependencies: - '@jest/types': 27.5.1 - '@types/node': 20.8.0 - chalk: 4.1.2 - jest-message-util: 27.5.1 - jest-util: 27.5.1 - slash: 3.0.0 - dev: true + '@humanwhocodes/config-array@0.13.0': + resolution: {integrity: sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==} + engines: {node: '>=10.10.0'} + deprecated: Use @eslint/config-array instead - /@jest/core@27.5.1(ts-node@10.9.1): - resolution: - { - integrity: sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/object-schema@2.0.3': + resolution: {integrity: sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==} + deprecated: Use @eslint/object-schema instead + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@istanbuljs/load-nyc-config@1.1.0': + resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} + engines: {node: '>=8'} + + '@istanbuljs/schema@0.1.3': + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + + '@jest/console@30.2.0': + resolution: {integrity: sha512-+O1ifRjkvYIkBqASKWgLxrpEhQAAE7hY77ALLUufSk5717KfOShg6IbqLmdsLMPdUiFvA2kTs0R7YZy+l0IzZQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/core@30.2.0': + resolution: {integrity: sha512-03W6IhuhjqTlpzh/ojut/pDB2LPRygyWX8ExpgHtQA8H/3K7+1vKmcINx5UzeOX1se6YEsBsOHQ1CRzf3fOwTQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + '@jest/diff-sequences@30.0.1': + resolution: {integrity: sha512-n5H8QLDJ47QqbCNn5SuFjCRDrOLEZ0h8vAHCK5RL9Ls7Xa8AQLa/YxAc9UjFqoEDM48muwtBGjtMY5cr0PLDCw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/environment-jsdom-abstract@30.2.0': + resolution: {integrity: sha512-kazxw2L9IPuZpQ0mEt9lu9Z98SqR74xcagANmMBU16X0lS23yPc0+S6hGLUz8kVRlomZEs/5S/Zlpqwf5yu6OQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + canvas: ^3.0.0 + jsdom: '*' + peerDependenciesMeta: + canvas: + optional: true + + '@jest/environment@30.2.0': + resolution: {integrity: sha512-/QPTL7OBJQ5ac09UDRa3EQes4gt1FTEG/8jZ/4v5IVzx+Cv7dLxlVIvfvSVRiiX2drWyXeBjkMSR8hvOWSog5g==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/expect-utils@30.2.0': + resolution: {integrity: sha512-1JnRfhqpD8HGpOmQp180Fo9Zt69zNtC+9lR+kT7NVL05tNXIi+QC8Csz7lfidMoVLPD3FnOtcmp0CEFnxExGEA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/expect@30.2.0': + resolution: {integrity: sha512-V9yxQK5erfzx99Sf+7LbhBwNWEZ9eZay8qQ9+JSC0TrMR1pMDHLMY+BnVPacWU6Jamrh252/IKo4F1Xn/zfiqA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/fake-timers@30.2.0': + resolution: {integrity: sha512-HI3tRLjRxAbBy0VO8dqqm7Hb2mIa8d5bg/NJkyQcOk7V118ObQML8RC5luTF/Zsg4474a+gDvhce7eTnP4GhYw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/get-type@30.1.0': + resolution: {integrity: sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/globals@30.2.0': + resolution: {integrity: sha512-b63wmnKPaK+6ZZfpYhz9K61oybvbI1aMcIs80++JI1O1rR1vaxHUCNqo3ITu6NU0d4V34yZFoHMn/uoKr/Rwfw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/pattern@30.0.1': + resolution: {integrity: sha512-gWp7NfQW27LaBQz3TITS8L7ZCQ0TLvtmI//4OwlQRx4rnWxcPNIYjxZpDcN4+UlGxgm3jS5QPz8IPTCkb59wZA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/reporters@30.2.0': + resolution: {integrity: sha512-DRyW6baWPqKMa9CzeiBjHwjd8XeAyco2Vt8XbcLFjiwCOEKOvy82GJ8QQnJE9ofsxCMPjH4MfH8fCWIHHDKpAQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 peerDependenciesMeta: node-notifier: optional: true + + '@jest/schemas@29.6.3': + resolution: {integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/schemas@30.0.5': + resolution: {integrity: sha512-DmdYgtezMkh3cpU8/1uyXakv3tJRcmcXxBOcO0tbaozPwpmh4YMsnWrQm9ZmZMfa5ocbxzbFk6O4bDPEc/iAnA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/snapshot-utils@30.2.0': + resolution: {integrity: sha512-0aVxM3RH6DaiLcjj/b0KrIBZhSX1373Xci4l3cW5xiUWPctZ59zQ7jj4rqcJQ/Z8JuN/4wX3FpJSa3RssVvCug==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/source-map@30.0.1': + resolution: {integrity: sha512-MIRWMUUR3sdbP36oyNyhbThLHyJ2eEDClPCiHVbrYAe5g3CHRArIVpBw7cdSB5fr+ofSfIb2Tnsw8iEHL0PYQg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/test-result@30.2.0': + resolution: {integrity: sha512-RF+Z+0CCHkARz5HT9mcQCBulb1wgCP3FBvl9VFokMX27acKphwyQsNuWH3c+ojd1LeWBLoTYoxF0zm6S/66mjg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/test-sequencer@30.2.0': + resolution: {integrity: sha512-wXKgU/lk8fKXMu/l5Hog1R61bL4q5GCdT6OJvdAFz1P+QrpoFuLU68eoKuVc4RbrTtNnTL5FByhWdLgOPSph+Q==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/transform@30.2.0': + resolution: {integrity: sha512-XsauDV82o5qXbhalKxD7p4TZYYdwcaEXC77PPD2HixEFF+6YGppjrAAQurTl2ECWcEomHBMMNS9AH3kcCFx8jA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jest/types@29.6.3': + resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + '@jest/types@30.2.0': + resolution: {integrity: sha512-H9xg1/sfVvyfU7o3zMfBEjQ1gcsdeTMgqHoYdN79tuLqfTtuu7WckRA1R5whDwOzxaZAeMKTYWqP+WCAi0CHsg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/gen-mapping@0.3.3': + resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + engines: {node: '>=6.0.0'} + + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.1.2': + resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/source-map@0.3.11': + resolution: {integrity: sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@jsonjoy.com/base64@1.1.2': + resolution: {integrity: sha512-q6XAnWQDIMA3+FTiOYajoYqySkO+JSat0ytXGSuRdq9uXE7o92gzuQwQM14xaCRlBLGq3v5miDGC4vkVTn54xA==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/json-pack@1.0.4': + resolution: {integrity: sha512-aOcSN4MeAtFROysrbqG137b7gaDDSmVrl5mpo6sT/w+kcXpWnzhMjmY/Fh/sDx26NBxyIE7MB1seqLeCAzy9Sg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@jsonjoy.com/util@1.2.0': + resolution: {integrity: sha512-4B8B+3vFsY4eo33DMKyJPlQ3sBMpPFUZK2dr3O3rXrOGKKbYG44J0XSFkDo1VOQiri5HFEhIeVvItjR2xcazmg==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + '@kurkle/color@0.3.4': + resolution: {integrity: sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==} + + '@mdi/js@7.4.47': + resolution: {integrity: sha512-KPnNOtm5i2pMabqZxpUz7iQf+mfrYZyKCZ8QNz85czgEt7cuHcGorWfdzUMWYA0SD+a6Hn4FmJ+YhzzzjkTZrQ==} + + '@napi-rs/wasm-runtime@0.2.12': + resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} + + '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1': + resolution: {integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@npmcli/fs@1.1.1': + resolution: {integrity: sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==} + + '@npmcli/move-file@1.1.2': + resolution: {integrity: sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==} + engines: {node: '>=10'} + deprecated: This functionality has been moved to @npmcli/fs + + '@nuxt/babel-preset-app@2.18.1': + resolution: {integrity: sha512-7AYAGVjykrvta7k+koMGbt6y6PTMwl74PX2i9Ubyc1VC9ewy9U/b6cW0gVJOR/ZJWPzaABAgVZC7N58PprUDfA==} + engines: {node: ^14.18.0 || >=16.10.0} + + '@nuxt/builder@2.18.1': + resolution: {integrity: sha512-hc4AUP3Nvov7jL0BEP7jFXt8zOfa6gt+y1kyoVvU1WHEVNcWnrGtRKvJuCwi1IwCVlx7Weh+luvHI4nzQwEeKg==} + engines: {node: ^14.18.0 || >=16.10.0} + + '@nuxt/cli@2.18.1': + resolution: {integrity: sha512-ZOoDlE4Fw1Cum6oG8DVnb7B4ivovXySxdDI8vnIt49Ypx22pBGt5y2ErF7g+5TAxGMIHpyh7peJWJwYp88PqPA==} + engines: {node: ^14.18.0 || >=16.10.0} + hasBin: true + + '@nuxt/components@2.2.1': + resolution: {integrity: sha512-r1LHUzifvheTnJtYrMuA+apgsrEJbxcgFKIimeXKb+jl8TnPWdV3egmrxBCaDJchrtY/wmHyP47tunsft7AWwg==} + peerDependencies: + consola: '*' + + '@nuxt/config@2.18.1': + resolution: {integrity: sha512-CTsUMFtNCJ6+7AkgMRz53zM9vxmsMYVJWBQOnikVzwFxm/jsWzjyXkp3pQb5/fNZuqR7qXmpUKIRtrdeUeN4JQ==} + engines: {node: ^14.18.0 || >=16.10.0} + + '@nuxt/core@2.18.1': + resolution: {integrity: sha512-BFnKVH7caEdDrK04qQ2U9F4Rf4hV/BqqXBJiIeHp7vM9CLKjTL5/yhiognDw3SBefmSJkpOATx1HJl3XM8c4fg==} + engines: {node: ^14.18.0 || >=16.10.0} + + '@nuxt/devalue@2.0.2': + resolution: {integrity: sha512-GBzP8zOc7CGWyFQS6dv1lQz8VVpz5C2yRszbXufwG/9zhStTIH50EtD87NmWbTMwXDvZLNg8GIpb1UFdH93JCA==} + + '@nuxt/friendly-errors-webpack-plugin@2.6.0': + resolution: {integrity: sha512-3IZj6MXbzlvUxDncAxgBMLQwGPY/JlNhy2i+AGyOHCAReR5HcBxYjVRBvyaKM9R3s5k4OODYKeHAbrToZH/47w==} + engines: {node: '>=14.18.0', npm: '>=5.0.0'} + peerDependencies: + webpack: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + + '@nuxt/generator@2.18.1': + resolution: {integrity: sha512-kZMfB5Ymvd/5ek+xfk2svQiMJWEAjZf5XNFTG+2WiNsitHb01Bo3W2QGidy+dwfuLtHoiOJkMovRlyAKWxTohg==} + engines: {node: ^14.18.0 || >=16.10.0} + + '@nuxt/kit@3.12.2': + resolution: {integrity: sha512-5kOqEzfc3FsAncjK2je7vuq4/QsR5ypViTnop52mlFLf0Ku1NMCrWCSWYowAh4P0yqTACMAZYa+HdRZHscU84g==} + engines: {node: ^14.18.0 || >=16.10.0} + + '@nuxt/kit@3.7.4': + resolution: {integrity: sha512-/S5abZL62BITCvC/TY3KWA6N721U1Osln3cQdBb56XHIeafZCBVqTi92Xb0o7ovl72mMRhrKwRu7elzvz9oT/g==} + engines: {node: ^14.18.0 || >=16.10.0} + + '@nuxt/loading-screen@2.0.4': + resolution: {integrity: sha512-xpEDAoRu75tLUYCkUJCIvJkWJSuwr8pqomvQ+fkXpSrkxZ/9OzlBFjAbVdOAWTMj4aV/LVQso4vcEdircKeFIQ==} + + '@nuxt/opencollective@0.4.0': + resolution: {integrity: sha512-uUsxOcO2lFeotV+BGOwNLeau+U17mhpaCRhE7v8nJLdWJ2iErQXadl28HaHe6btuT8RD0LDSpvwCiKrHznDxUA==} + engines: {node: '>=8.0.0', npm: '>=5.0.0'} + hasBin: true + + '@nuxt/schema@3.12.2': + resolution: {integrity: sha512-IRBuOEPOIe1CANKnO2OUiqZ1Hp/0htPkLaigK7WT6ef/SdIFZUd68Tqqejqy2AFrbgU9G80k3U7eg2XUdaiQlQ==} + engines: {node: ^14.18.0 || >=16.10.0} + + '@nuxt/schema@3.7.4': + resolution: {integrity: sha512-q6js+97vDha4Fa2x2kDVEuokJr+CGIh1TY2wZp2PLZ7NhG3XEeib7x9Hq8XE8B6pD0GKBRy3eRPPOY69gekBCw==} + engines: {node: ^14.18.0 || >=16.10.0} + + '@nuxt/server@2.18.1': + resolution: {integrity: sha512-4GHmgi1NS6uCL+3QzlxmHmEoKkejQKTDrKPtA16w8iw/8EBgCrAkvXukcIMxF7Of+IYi1I/duVmCyferxo7jyw==} + engines: {node: ^14.18.0 || >=16.10.0} + + '@nuxt/telemetry@1.5.0': + resolution: {integrity: sha512-MhxiiYCFe0MayN2TvmpcsCV66zBePtrSVkFLJHwTFuneQ5Qma5x0NmCwdov7O4NSuTfgSZels9qPJh0zy0Kc4g==} + hasBin: true + + '@nuxt/types@2.18.1': + resolution: {integrity: sha512-PpReoV9oHCnSpB9WqemTUWmlH1kqFHC3Xe5LH904VvCl/3xLO2nGYcrHeZCMV5hXNWsDUyqDnd/2cQHmeqj5lA==} + engines: {node: ^14.18.0 || >=16.10.0} + + '@nuxt/typescript-build@3.0.2': + resolution: {integrity: sha512-IFSznjafW5xm0XHg9Q9aHVW7i9J2pAYfyorh3ro3Pf0OnCbS0acmwBnp2juza+DqNhZa1DhNentmUsgiYp730g==} + peerDependencies: + '@nuxt/types': '>=2.13.1' + typescript: 4.x || 5.x + + '@nuxt/ui-templates@1.3.1': + resolution: {integrity: sha512-5gc02Pu1HycOVUWJ8aYsWeeXcSTPe8iX8+KIrhyEtEoOSkY0eMBuo0ssljB8wALuEmepv31DlYe5gpiRwkjESA==} + + '@nuxt/utils@2.18.1': + resolution: {integrity: sha512-aWeB8VMhtymo5zXUiQaohCu8IqJqENF9iCag3wyJpdhpNDVoghGUJAl0F6mQvNTJgQzseFtf4XKqTfvcgVzyGg==} + engines: {node: ^14.18.0 || >=16.10.0} + + '@nuxt/vue-app@2.18.1': + resolution: {integrity: sha512-yxkunoTv6EVa42xM7qES0N1DNMo4UbP/s89L7HjqngQ4KzVWyyzK0qqJ9u3Gu4CabXhHFSquu11gtn+dylKyTA==} + engines: {node: ^14.18.0 || >=16.10.0} + + '@nuxt/vue-renderer@2.18.1': + resolution: {integrity: sha512-Nl8/IbV+sTEWCczHKcjLbZrFO6y5fCcFxZwd6Opatcbr2z380abwpDf3a9UjnVW3wPEM+/xoy1/MBCLY3VmWcw==} + engines: {node: ^14.18.0 || >=16.10.0} + + '@nuxt/webpack@2.18.1': + resolution: {integrity: sha512-6EqbIoheLAJ0E7dfQB5ftOKL4d74N98dFMY3q89QTaoS9VXBFB5D1MLd27WuyfhChmzuHRwHfjaBW8QFdhjwew==} + engines: {node: ^14.18.0 || >=16.10.0} + + '@nuxtjs/dotenv@1.4.2': + resolution: {integrity: sha512-/3+Cw5qLNIQD8ZvXLJG1suxvfC4ltlUuYegOwirHrLrzptHh/+rCkBXrNbrz2qAiwc+/yK91XjZGGzNM1dFmCw==} + + '@nuxtjs/eslint-config-typescript@12.1.0': + resolution: {integrity: sha512-l2fLouDYwdAvCZEEw7wGxOBj+i8TQcHFu3zMPTLqKuv1qu6WcZIr0uztkbaa8ND1uKZ9YPqKx6UlSOjM4Le69Q==} + peerDependencies: + eslint: ^8.48.0 + + '@nuxtjs/eslint-config@12.0.0': + resolution: {integrity: sha512-ewenelo75x0eYEUK+9EBXjc/OopQCvdkmYmlZuoHq5kub/vtiRpyZ/autppwokpHUq8tiVyl2ejMakoiHiDTrg==} + peerDependencies: + eslint: ^8.23.0 + + '@nuxtjs/eslint-module@4.1.0': + resolution: {integrity: sha512-lW9ozEjOrnU8Uot3GOAZ/0ThNAds0d6UAp9n46TNxcTvH/MOcAggGbMNs16c0HYT2HlyPQvXORCHQ5+9p87mmw==} + peerDependencies: + eslint: '>=7' + + '@nuxtjs/firebase@8.2.2': + resolution: {integrity: sha512-j+kW0utwq23w71D0I4RyOc9/eYGe8WpsoI2GD9PT744rMWmj4MFHASjmgyDPk2KdZGxsknxUW6yq29aLd0E2ow==} + peerDependencies: + firebase: ^9.6.2 + nuxt: ^2.15.6 + + '@nuxtjs/sitemap@2.4.0': + resolution: {integrity: sha512-TVgIYOtPp7KAfaUo76WRpGbO20j4D/xi/A7shFIGjARHs+FvfAWXNCtBT87dTwe/RoYzAsEKtijFFUTaSu5bUA==} + engines: {node: '>=8.9.0', npm: '>=5.0.0'} + + '@nuxtjs/stylelint-module@5.2.0': + resolution: {integrity: sha512-CMGZORt5fM1pK+5Xj3p2uajkK9DZ9Sja7jewXa8LZFNMjt7GIsKaoAvH4poCUMorhIVBS0lGQZ9BlRmg3MWxvg==} + peerDependencies: + stylelint: '>=13' + + '@nuxtjs/vuetify@1.12.3': + resolution: {integrity: sha512-6uVL3cfESMB00eVjJTNkyU4jvuPTGPn1yteo7lQTH6v+fxHcPaOgvzVYHIKSHIz1DecuOiB5c9b+YjsRP5+C8A==} + + '@nuxtjs/youch@4.2.3': + resolution: {integrity: sha512-XiTWdadTwtmL/IGkNqbVe+dOlT+IMvcBu7TvKI7plWhVQeBCQ9iKhk3jgvVWFyiwL2yHJDlEwOM5v9oVES5Xmw==} + + '@one-ini/wasm@0.1.1': + resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==} + + '@panva/asn1.js@1.0.0': + resolution: {integrity: sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==} + engines: {node: '>=10.13.0'} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@pkgr/core@0.2.9': + resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + + '@polka/url@1.0.0-next.23': + resolution: {integrity: sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg==} + + '@protobufjs/aspromise@1.1.2': + resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} + + '@protobufjs/base64@1.1.2': + resolution: {integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==} + + '@protobufjs/codegen@2.0.4': + resolution: {integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==} + + '@protobufjs/eventemitter@1.1.0': + resolution: {integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==} + + '@protobufjs/fetch@1.1.0': + resolution: {integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==} + + '@protobufjs/float@1.0.2': + resolution: {integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==} + + '@protobufjs/inquire@1.1.0': + resolution: {integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==} + + '@protobufjs/path@1.1.2': + resolution: {integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==} + + '@protobufjs/pool@1.1.0': + resolution: {integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==} + + '@protobufjs/utf8@1.1.0': + resolution: {integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==} + + '@rollup/pluginutils@4.2.1': + resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} + engines: {node: '>= 8.0.0'} + + '@rollup/pluginutils@5.0.4': + resolution: {integrity: sha512-0KJnIoRI8A+a1dqOYLxH8vBf8bphDmty5QvIm2hqm7oFCFYKCAZWWd2hXgMibaPsNDhI0AtpYfQZJG47pt/k4g==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@rollup/pluginutils@5.1.0': + resolution: {integrity: sha512-XTIWOPPcpvyKI6L1NHo0lFlCyznUEyPmPY1mc3KpPVDYulHSTvyeLNVW00QTLIAFNhR3kYnJTQHeGqU4M3n09g==} + engines: {node: '>=14.0.0'} + peerDependencies: + rollup: ^1.20.0||^2.0.0||^3.0.0||^4.0.0 + peerDependenciesMeta: + rollup: + optional: true + + '@sinclair/typebox@0.27.8': + resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + + '@sinclair/typebox@0.34.41': + resolution: {integrity: sha512-6gS8pZzSXdyRHTIqoqSVknxolr1kzfy4/CeDnrzsVz8TTIWUbOBr6gnzOmTYJ3eXQNh4IYHIGi5aIL7sOZ2G/g==} + + '@sindresorhus/merge-streams@2.3.0': + resolution: {integrity: sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==} + engines: {node: '>=18'} + + '@sinonjs/commons@3.0.1': + resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} + + '@sinonjs/fake-timers@13.0.5': + resolution: {integrity: sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==} + + '@tootallnate/once@2.0.0': + resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} + engines: {node: '>= 10'} + + '@trysound/sax@0.2.0': + resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} + engines: {node: '>=10.13.0'} + + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/body-parser@1.19.3': + resolution: {integrity: sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==} + + '@types/compression@1.7.5': + resolution: {integrity: sha512-AAQvK5pxMpaT+nDvhHrsBhLSYG5yQdtkaJE1WYieSNY2mVFKAgmU4ks65rkZD5oqnGCFLyQpUr1CqI4DmUMyDg==} + + '@types/connect@3.4.38': + resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} + + '@types/eslint-scope@3.7.7': + resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==} + + '@types/eslint@8.44.3': + resolution: {integrity: sha512-iM/WfkwAhwmPff3wZuPLYiHX18HI24jU8k1ZSH7P8FHwxTjZ2P6CoX2wnF43oprR+YXJM6UUxATkNvyv/JHd+g==} + + '@types/eslint@9.6.1': + resolution: {integrity: sha512-FXx2pKgId/WyYo2jXw63kk7/+TY7u7AziEJxJAnSFzHlqTAS3Ync6SvgYAN/k4/PQpnnVuzoMuVnByKK2qp0ag==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/etag@1.8.3': + resolution: {integrity: sha512-QYHv9Yeh1ZYSMPQOoxY4XC4F1r+xRUiAriB303F4G6uBsT3KKX60DjiogvVv+2VISVDuJhcIzMdbjT+Bm938QQ==} + + '@types/express-serve-static-core@4.17.37': + resolution: {integrity: sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==} + + '@types/express@4.17.18': + resolution: {integrity: sha512-Sxv8BSLLgsBYmcnGdGjjEjqET2U+AKAdCRODmMiq02FgjwuV75Ut85DRpvFjyw/Mk0vgUOliGRU0UUmuuZHByQ==} + + '@types/file-loader@5.0.4': + resolution: {integrity: sha512-aB4X92oi5D2nIGI8/kolnJ47btRM2MQjQS4eJgA/VnCD12x0+kP5v7b5beVQWKHLOcquwUXvv6aMt8PmMy9uug==} + + '@types/html-minifier-terser@5.1.2': + resolution: {integrity: sha512-h4lTMgMJctJybDp8CQrxTUiiYmedihHWkjnF/8Pxseu2S6Nlfcy8kwboQ8yejh456rP2yWoEVm1sS/FVsfM48w==} + + '@types/html-minifier-terser@7.0.2': + resolution: {integrity: sha512-mm2HqV22l8lFQh4r2oSsOEVea+m0qqxEmwpc9kC1p/XzmjLWrReR9D/GRs8Pex2NX/imyEH9c5IU/7tMBQCHOA==} + + '@types/http-errors@2.0.4': + resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} + + '@types/istanbul-lib-coverage@2.0.6': + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + + '@types/istanbul-lib-report@3.0.3': + resolution: {integrity: sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==} + + '@types/istanbul-reports@3.0.4': + resolution: {integrity: sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==} + + '@types/jsdom@21.1.7': + resolution: {integrity: sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/jsonwebtoken@8.5.9': + resolution: {integrity: sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==} + + '@types/less@3.0.6': + resolution: {integrity: sha512-PecSzorDGdabF57OBeQO/xFbAkYWo88g4Xvnsx7LRwqLC17I7OoKtA3bQB9uXkY6UkMWCOsA8HSVpaoitscdXw==} + + '@types/long@4.0.2': + resolution: {integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==} + + '@types/mime@1.3.3': + resolution: {integrity: sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg==} + + '@types/minimist@1.2.3': + resolution: {integrity: sha512-ZYFzrvyWUNhaPomn80dsMNgMeXxNWZBdkuG/hWlUvXvbdUH8ZERNBGXnU87McuGcWDsyzX2aChCv/SVN348k3A==} + + '@types/node@12.20.55': + resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} + + '@types/node@16.18.55': + resolution: {integrity: sha512-Y1zz/LIuJek01+hlPNzzXQhmq/Z2BCP96j18MSXC0S0jSu/IG4FFxmBs7W4/lI2vPJ7foVfEB0hUVtnOjnCiTg==} + + '@types/node@24.6.2': + resolution: {integrity: sha512-d2L25Y4j+W3ZlNAeMKcy7yDsK425ibcAOO2t7aPTz6gNMH0z2GThtwENCDc0d/Pw9wgyRqE5Px1wkV7naz8ang==} + + '@types/node@25.1.0': + resolution: {integrity: sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA==} + + '@types/normalize-package-data@2.4.2': + resolution: {integrity: sha512-lqa4UEhhv/2sjjIQgjX8B+RBjj47eo0mzGasklVJ78UKGQY1r0VpB9XHDaZZO9qzEFDdy4MrXLuEaSmPrPSe/A==} + + '@types/optimize-css-assets-webpack-plugin@5.0.8': + resolution: {integrity: sha512-n134DdmRVXTy0KKbgg3A/G02r2XJKJicYzbJYhdIO8rdYdzoMv6GNHjog2Oq1ttaCOhsYcPIA6Sn7eFxEGCM1A==} + + '@types/parse-json@4.0.0': + resolution: {integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==} + + '@types/pug@2.0.10': + resolution: {integrity: sha512-Sk/uYFOBAB7mb74XcpizmH0KOR2Pv3D2Hmrh1Dmy5BmK3MpdSa5kqZcg6EKBdklU0bFXX9gCfzvpnyUehrPIuA==} + + '@types/qrcode@1.5.6': + resolution: {integrity: sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw==} + + '@types/qs@6.9.8': + resolution: {integrity: sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==} + + '@types/range-parser@1.2.5': + resolution: {integrity: sha512-xrO9OoVPqFuYyR/loIHjnbvvyRZREYKLjxV4+dY6v3FQR3stQ9ZxIGkaclF7YhI9hfjpuTbu14hZEy94qKLtOA==} + + '@types/sax@1.2.7': + resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==} + + '@types/semver@7.5.3': + resolution: {integrity: sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==} + + '@types/send@0.17.2': + resolution: {integrity: sha512-aAG6yRf6r0wQ29bkS+x97BIs64ZLxeE/ARwyS6wrldMm3C1MdKwCcnnEwMC1slI8wuxJOpiUH9MioC0A0i+GJw==} + + '@types/serve-static@1.15.7': + resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} + + '@types/source-list-map@0.1.3': + resolution: {integrity: sha512-I9R/7fUjzUOyDy6AFkehCK711wWoAXEaBi80AfjZt1lIkbe6AcXKd3ckQc3liMvQExWvfOeh/8CtKzrfUFN5gA==} + + '@types/stack-utils@2.0.3': + resolution: {integrity: sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==} + + '@types/strip-bom@3.0.0': + resolution: {integrity: sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==} + + '@types/strip-json-comments@0.0.30': + resolution: {integrity: sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==} + + '@types/tapable@1.0.9': + resolution: {integrity: sha512-fOHIwZua0sRltqWzODGUM6b4ffZrf/vzGUmNXdR+4DzuJP42PMbM5dLKcdzlYvv8bMJ3GALOzkk1q7cDm2zPyA==} + + '@types/terser-webpack-plugin@4.2.1': + resolution: {integrity: sha512-x688KsgQKJF8PPfv4qSvHQztdZNHLlWJdolN9/ptAGimHVy3rY+vHdfglQDFh1Z39h7eMWOd6fQ7ke3PKQcdyA==} + + '@types/tough-cookie@4.0.5': + resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} + + '@types/uglify-js@3.17.2': + resolution: {integrity: sha512-9SjrHO54LINgC/6Ehr81NjAxAYvwEZqjUHLjJYvC4Nmr9jbLQCIZbWSvl4vXQkkmR1UAuaKDycau3O1kWGFyXQ==} + + '@types/webpack-bundle-analyzer@3.9.5': + resolution: {integrity: sha512-QlyDyX7rsOIJHASzXWlih8DT9fR+XCG9cwIV/4pKrtScdHv4XFshdEf/7iiqLqG0lzWcoBdzG8ylMHQ5XLNixw==} + + '@types/webpack-hot-middleware@2.25.5': + resolution: {integrity: sha512-/eRWWMgZteNzl17qLCRdRmtKPZuWy984b11Igz9+BAU5a99Hc2AJinnMohMPVahGRSHby4XwsnjlgIt9m0Ce3g==} + + '@types/webpack-sources@3.2.1': + resolution: {integrity: sha512-iLC3Fsx62ejm3ST3PQ8vBMC54Rb3EoCprZjeJGI5q+9QjfDLGt9jeg/k245qz1G9AQnORGk0vqPicJFPT1QODQ==} + + '@types/webpack@4.41.38': + resolution: {integrity: sha512-oOW7E931XJU1mVfCnxCVgv8GLFL768pDO5u2Gzk82i8yTIgX6i7cntyZOkZYb/JtYM8252SN9bQp9tgkVDSsRw==} + + '@types/yargs-parser@21.0.3': + resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} + + '@types/yargs@17.0.33': + resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} + + '@typescript-eslint/eslint-plugin@6.7.3': + resolution: {integrity: sha512-vntq452UHNltxsaaN+L9WyuMch8bMd9CqJ3zhzTPXXidwbf5mqqKCVXEuvRZUqLJSTLeWE65lQwyXsRGnXkCTA==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/parser@6.7.3': + resolution: {integrity: sha512-TlutE+iep2o7R8Lf+yoer3zU6/0EAUc8QIBB3GYBc1KGz4c4TRm83xwXUZVPlZ6YCLss4r77jbu6j3sendJoiQ==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/scope-manager@6.7.3': + resolution: {integrity: sha512-wOlo0QnEou9cHO2TdkJmzF7DFGvAKEnB82PuPNHpT8ZKKaZu6Bm63ugOTn9fXNJtvuDPanBc78lGUGGytJoVzQ==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@typescript-eslint/type-utils@6.7.3': + resolution: {integrity: sha512-Fc68K0aTDrKIBvLnKTZ5Pf3MXK495YErrbHb1R6aTpfK5OdSFj0rVN7ib6Tx6ePrZ2gsjLqr0s98NG7l96KSQw==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/types@6.7.3': + resolution: {integrity: sha512-4g+de6roB2NFcfkZb439tigpAMnvEIg3rIjWQ+EM7IBaYt/CdJt6em9BJ4h4UpdgaBWdmx2iWsafHTrqmgIPNw==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@typescript-eslint/typescript-estree@6.7.3': + resolution: {integrity: sha512-YLQ3tJoS4VxLFYHTw21oe1/vIZPRqAO91z6Uv0Ss2BKm/Ag7/RVQBcXTGcXhgJMdA4U+HrKuY5gWlJlvoaKZ5g==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + '@typescript-eslint/utils@6.7.3': + resolution: {integrity: sha512-vzLkVder21GpWRrmSR9JxGZ5+ibIUSudXlW52qeKpzUEQhRSmyZiVDDj3crAth7+5tmN1ulvgKaCU2f/bPRCzg==} + engines: {node: ^16.0.0 || >=18.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + + '@typescript-eslint/visitor-keys@6.7.3': + resolution: {integrity: sha512-HEVXkU9IB+nk9o63CeICMHxFWbHWr3E1mpilIQBe9+7L/lH97rleFLVtYsfnWB+JVMaiFnEaxvknvmIzX+CqVg==} + engines: {node: ^16.0.0 || >=18.0.0} + + '@ungap/structured-clone@1.2.0': + resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==} + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + '@unrs/resolver-binding-android-arm-eabi@1.11.1': + resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} + cpu: [arm] + os: [android] + + '@unrs/resolver-binding-android-arm64@1.11.1': + resolution: {integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==} + cpu: [arm64] + os: [android] + + '@unrs/resolver-binding-darwin-arm64@1.11.1': + resolution: {integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==} + cpu: [arm64] + os: [darwin] + + '@unrs/resolver-binding-darwin-x64@1.11.1': + resolution: {integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==} + cpu: [x64] + os: [darwin] + + '@unrs/resolver-binding-freebsd-x64@1.11.1': + resolution: {integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==} + cpu: [x64] + os: [freebsd] + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + resolution: {integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + resolution: {integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} + cpu: [ppc64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} + cpu: [s390x] + os: [linux] + + '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-linux-x64-musl@1.11.1': + resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-wasm32-wasi@1.11.1': + resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + resolution: {integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==} + cpu: [arm64] + os: [win32] + + '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + resolution: {integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==} + cpu: [ia32] + os: [win32] + + '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + resolution: {integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==} + cpu: [x64] + os: [win32] + + '@vue/babel-helper-vue-jsx-merge-props@1.4.0': + resolution: {integrity: sha512-JkqXfCkUDp4PIlFdDQ0TdXoIejMtTHP67/pvxlgeY+u5k3LEdKuWZ3LK6xkxo52uDoABIVyRwqVkfLQJhk7VBA==} + + '@vue/babel-plugin-transform-vue-jsx@1.4.0': + resolution: {integrity: sha512-Fmastxw4MMx0vlgLS4XBX0XiBbUFzoMGeVXuMV08wyOfXdikAFqBTuYPR0tlk+XskL19EzHc39SgjrPGY23JnA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@vue/babel-preset-jsx@1.4.0': + resolution: {integrity: sha512-QmfRpssBOPZWL5xw7fOuHNifCQcNQC1PrOo/4fu6xlhlKJJKSA3HqX92Nvgyx8fqHZTUGMPHmFA+IDqwXlqkSA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + vue: '*' + peerDependenciesMeta: + vue: + optional: true + + '@vue/babel-sugar-composition-api-inject-h@1.4.0': + resolution: {integrity: sha512-VQq6zEddJHctnG4w3TfmlVp5FzDavUSut/DwR0xVoe/mJKXyMcsIibL42wPntozITEoY90aBV0/1d2KjxHU52g==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@vue/babel-sugar-composition-api-render-instance@1.4.0': + resolution: {integrity: sha512-6ZDAzcxvy7VcnCjNdHJ59mwK02ZFuP5CnucloidqlZwVQv5CQLijc3lGpR7MD3TWFi78J7+a8J56YxbCtHgT9Q==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@vue/babel-sugar-functional-vue@1.4.0': + resolution: {integrity: sha512-lTEB4WUFNzYt2In6JsoF9sAYVTo84wC4e+PoZWSgM6FUtqRJz7wMylaEhSRgG71YF+wfLD6cc9nqVeXN2rwBvw==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@vue/babel-sugar-inject-h@1.4.0': + resolution: {integrity: sha512-muwWrPKli77uO2fFM7eA3G1lAGnERuSz2NgAxuOLzrsTlQl8W4G+wwbM4nB6iewlKbwKRae3nL03UaF5ffAPMA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@vue/babel-sugar-v-model@1.4.0': + resolution: {integrity: sha512-0t4HGgXb7WHYLBciZzN5s0Hzqan4Ue+p/3FdQdcaHAb7s5D9WZFGoSxEZHrR1TFVZlAPu1bejTKGeAzaaG3NCQ==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@vue/babel-sugar-v-on@1.4.0': + resolution: {integrity: sha512-m+zud4wKLzSKgQrWwhqRObWzmTuyzl6vOP7024lrpeJM4x2UhQtRDLgYjXAw9xBXjCwS0pP9kXjg91F9ZNo9JA==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@vue/compiler-sfc@2.7.16': + resolution: {integrity: sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==} + + '@vue/component-compiler-utils@3.3.0': + resolution: {integrity: sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ==} + + '@vue/test-utils@1.3.6': + resolution: {integrity: sha512-udMmmF1ts3zwxUJEIAj5ziioR900reDrt6C9H3XpWPsLBx2lpHKoA4BTdd9HNIYbkGltWw+JjWJ+5O6QBwiyEw==} + peerDependencies: + vue: 2.x + vue-template-compiler: ^2.x + + '@webassemblyjs/ast@1.14.1': + resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} + + '@webassemblyjs/ast@1.9.0': + resolution: {integrity: sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==} + + '@webassemblyjs/floating-point-hex-parser@1.13.2': + resolution: {integrity: sha512-6oXyTOzbKxGH4steLbLNOu71Oj+C8Lg34n6CqRvqfS2O71BxY6ByfMDRhBytzknj9yGUPVJ1qIKhRlAwO1AovA==} + + '@webassemblyjs/floating-point-hex-parser@1.9.0': + resolution: {integrity: sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==} + + '@webassemblyjs/helper-api-error@1.13.2': + resolution: {integrity: sha512-U56GMYxy4ZQCbDZd6JuvvNV/WFildOjsaWD3Tzzvmw/mas3cXzRJPMjP83JqEsgSbyrmaGjBfDtV7KDXV9UzFQ==} + + '@webassemblyjs/helper-api-error@1.9.0': + resolution: {integrity: sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==} + + '@webassemblyjs/helper-buffer@1.14.1': + resolution: {integrity: sha512-jyH7wtcHiKssDtFPRB+iQdxlDf96m0E39yb0k5uJVhFGleZFoNw1c4aeIcVUPPbXUVJ94wwnMOAqUHyzoEPVMA==} + + '@webassemblyjs/helper-buffer@1.9.0': + resolution: {integrity: sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==} + + '@webassemblyjs/helper-code-frame@1.9.0': + resolution: {integrity: sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==} + + '@webassemblyjs/helper-fsm@1.9.0': + resolution: {integrity: sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==} + + '@webassemblyjs/helper-module-context@1.9.0': + resolution: {integrity: sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==} + + '@webassemblyjs/helper-numbers@1.13.2': + resolution: {integrity: sha512-FE8aCmS5Q6eQYcV3gI35O4J789wlQA+7JrqTTpJqn5emA4U2hvwJmvFRC0HODS+3Ye6WioDklgd6scJ3+PLnEA==} + + '@webassemblyjs/helper-wasm-bytecode@1.13.2': + resolution: {integrity: sha512-3QbLKy93F0EAIXLh0ogEVR6rOubA9AoZ+WRYhNbFyuB70j3dRdwH9g+qXhLAO0kiYGlg3TxDV+I4rQTr/YNXkA==} + + '@webassemblyjs/helper-wasm-bytecode@1.9.0': + resolution: {integrity: sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==} + + '@webassemblyjs/helper-wasm-section@1.14.1': + resolution: {integrity: sha512-ds5mXEqTJ6oxRoqjhWDU83OgzAYjwsCV8Lo/N+oRsNDmx/ZDpqalmrtgOMkHwxsG0iI//3BwWAErYRHtgn0dZw==} + + '@webassemblyjs/helper-wasm-section@1.9.0': + resolution: {integrity: sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==} + + '@webassemblyjs/ieee754@1.13.2': + resolution: {integrity: sha512-4LtOzh58S/5lX4ITKxnAK2USuNEvpdVV9AlgGQb8rJDHaLeHciwG4zlGr0j/SNWlr7x3vO1lDEsuePvtcDNCkw==} + + '@webassemblyjs/ieee754@1.9.0': + resolution: {integrity: sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==} + + '@webassemblyjs/leb128@1.13.2': + resolution: {integrity: sha512-Lde1oNoIdzVzdkNEAWZ1dZ5orIbff80YPdHx20mrHwHrVNNTjNr8E3xz9BdpcGqRQbAEa+fkrCb+fRFTl/6sQw==} + + '@webassemblyjs/leb128@1.9.0': + resolution: {integrity: sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==} + + '@webassemblyjs/utf8@1.13.2': + resolution: {integrity: sha512-3NQWGjKTASY1xV5m7Hr0iPeXD9+RDobLll3T9d2AO+g3my8xy5peVyjSag4I50mR1bBSN/Ct12lo+R9tJk0NZQ==} + + '@webassemblyjs/utf8@1.9.0': + resolution: {integrity: sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==} + + '@webassemblyjs/wasm-edit@1.14.1': + resolution: {integrity: sha512-RNJUIQH/J8iA/1NzlE4N7KtyZNHi3w7at7hDjvRNm5rcUXa00z1vRz3glZoULfJ5mpvYhLybmVcwcjGrC1pRrQ==} + + '@webassemblyjs/wasm-edit@1.9.0': + resolution: {integrity: sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==} + + '@webassemblyjs/wasm-gen@1.14.1': + resolution: {integrity: sha512-AmomSIjP8ZbfGQhumkNvgC33AY7qtMCXnN6bL2u2Js4gVCg8fp735aEiMSBbDR7UQIj90n4wKAFUSEd0QN2Ukg==} + + '@webassemblyjs/wasm-gen@1.9.0': + resolution: {integrity: sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==} + + '@webassemblyjs/wasm-opt@1.14.1': + resolution: {integrity: sha512-PTcKLUNvBqnY2U6E5bdOQcSM+oVP/PmrDY9NzowJjislEjwP/C4an2303MCVS2Mg9d3AJpIGdUFIQQWbPds0Sw==} + + '@webassemblyjs/wasm-opt@1.9.0': + resolution: {integrity: sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==} + + '@webassemblyjs/wasm-parser@1.14.1': + resolution: {integrity: sha512-JLBl+KZ0R5qB7mCnud/yyX08jWFw5MsoalJ1pQ4EdFlgj9VdXKGuENGsiCIjegI1W7p91rUlcB/LB5yRJKNTcQ==} + + '@webassemblyjs/wasm-parser@1.9.0': + resolution: {integrity: sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==} + + '@webassemblyjs/wast-parser@1.9.0': + resolution: {integrity: sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==} + + '@webassemblyjs/wast-printer@1.14.1': + resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} + + '@webassemblyjs/wast-printer@1.9.0': + resolution: {integrity: sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==} + + '@xtuc/ieee754@1.2.0': + resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} + + '@xtuc/long@4.2.2': + resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==} + + abbrev@1.1.1: + resolution: {integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==} + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + accepts@1.3.8: + resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} + engines: {node: '>= 0.6'} + + acorn-import-phases@1.0.4: + resolution: {integrity: sha512-wKmbr/DDiIXzEOiWrTTUcDm24kQ2vGfZQvM2fwg2vXqR5uW6aapr7ObPtj1th32b9u90/Pf4AItvdTh42fBmVQ==} + engines: {node: '>=10.13.0'} + peerDependencies: + acorn: ^8.14.0 + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn-walk@8.2.0: + resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} + engines: {node: '>=0.4.0'} + + acorn@6.4.2: + resolution: {integrity: sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==} + engines: {node: '>=0.4.0'} + hasBin: true + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} + engines: {node: '>= 14'} + + aggregate-error@3.1.0: + resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} + engines: {node: '>=8'} + + ajv-errors@1.0.1: + resolution: {integrity: sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==} + peerDependencies: + ajv: '>=5.0.0' + + ajv-formats@2.1.1: + resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + + ajv-keywords@3.5.2: + resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} + peerDependencies: + ajv: ^6.9.1 + + ajv-keywords@5.1.0: + resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} + peerDependencies: + ajv: ^8.8.2 + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ajv@8.12.0: + resolution: {integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==} + + ajv@8.17.1: + resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==} + + ansi-align@3.0.1: + resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==} + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-escapes@7.1.1: + resolution: {integrity: sha512-Zhl0ErHcSRUaVfGUeUdDuLgpkEo8KIFjB4Y9uAc46ScOpdDiU1Dbyplh7qWJeJ/ZHpbyMSM26+X3BySgnIz40Q==} + engines: {node: '>=18'} + + ansi-html-community@0.0.8: + resolution: {integrity: sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==} + engines: {'0': node >= 0.8.0} + hasBin: true + + ansi-regex@2.1.1: + resolution: {integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==} + engines: {node: '>=0.10.0'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + engines: {node: '>=12'} + + ansi-styles@2.2.1: + resolution: {integrity: sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==} + engines: {node: '>=0.10.0'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} + engines: {node: '>=12'} + + anymatch@2.0.0: + resolution: {integrity: sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + aproba@1.2.0: + resolution: {integrity: sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==} + + arg@4.1.3: + resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + argparse@1.0.10: + resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + arr-diff@4.0.0: + resolution: {integrity: sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==} + engines: {node: '>=0.10.0'} + + arr-flatten@1.1.0: + resolution: {integrity: sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==} + engines: {node: '>=0.10.0'} + + arr-union@3.1.0: + resolution: {integrity: sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==} + engines: {node: '>=0.10.0'} + + array-buffer-byte-length@1.0.0: + resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==} + + array-ify@1.0.0: + resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} + + array-includes@3.1.7: + resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==} + engines: {node: '>= 0.4'} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + array-unique@0.3.2: + resolution: {integrity: sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==} + engines: {node: '>=0.10.0'} + + array.prototype.findlastindex@1.2.3: + resolution: {integrity: sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.2: + resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.2: + resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==} + engines: {node: '>= 0.4'} + + array.prototype.reduce@1.0.6: + resolution: {integrity: sha512-UW+Mz8LG/sPSU8jRDCjVr6J/ZKAGpHfwrZ6kWTG5qCxIEiXdVshqGnu5vEZA8S1y6X4aCSbQZ0/EEsfvEvBiSg==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.2: + resolution: {integrity: sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==} + engines: {node: '>= 0.4'} + + arrify@1.0.1: + resolution: {integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==} + engines: {node: '>=0.10.0'} + + arrify@2.0.1: + resolution: {integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==} + engines: {node: '>=8'} + + asn1.js@5.4.1: + resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==} + + assert@1.5.1: + resolution: {integrity: sha512-zzw1uCAgLbsKwBfFc8CX78DDg+xZeBksSO3vwVIDDN5i94eOrPsSSyiVhmsSABFDM/OcpE2aagCat9dnWQLG1A==} + + assign-symbols@1.0.0: + resolution: {integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==} + engines: {node: '>=0.10.0'} + + astral-regex@2.0.0: + resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} + engines: {node: '>=8'} + + async-cache@1.1.0: + resolution: {integrity: sha512-YDQc4vBn5NFhY6g6HhVshyi3Fy9+SQ5ePnE7JLDJn1DoL+i7ER+vMwtTNOYk9leZkYMnOwpBCWqyLDPw8Aig8g==} + deprecated: No longer maintained. Use [lru-cache](http://npm.im/lru-cache) version 7.6 or higher, and provide an asynchronous `fetchMethod` option. + + async-each@1.0.6: + resolution: {integrity: sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==} + + async-retry@1.3.3: + resolution: {integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==} + + async@3.2.6: + resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + at-least-node@1.0.0: + resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} + engines: {node: '>= 4.0.0'} + + atob@2.1.2: + resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==} + engines: {node: '>= 4.5.0'} + hasBin: true + + autoprefixer@10.4.19: + resolution: {integrity: sha512-BaENR2+zBZ8xXhM4pUaKUxlVdxZ0EZhjvbopwnXmxRUfqDmwSpC2lAi/QXvx7NRdPCo1WKEcEF6mV64si1z4Ew==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + available-typed-arrays@1.0.5: + resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==} + engines: {node: '>= 0.4'} + + axios@0.31.0: + resolution: {integrity: sha512-HGIUj/P74co3rSLBV9SHz9LMgCmrXFEtkfMcC5r6bS5j3dBHUcAje2tS4fmU6WM20kuhvUX04XE58594dpgi1g==} + + babel-code-frame@6.26.0: + resolution: {integrity: sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==} + + babel-core@7.0.0-bridge.0: + resolution: {integrity: sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==} + peerDependencies: + '@babel/core': ^7.0.0-0 + + babel-jest@30.2.0: + resolution: {integrity: sha512-0YiBEOxWqKkSQWL9nNGGEgndoeL0ZpWrbLMNL5u/Kaxrli3Eaxlt3ZtIDktEvXt4L/R9r3ODr2zKwGM/2BjxVw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + '@babel/core': ^7.11.0 || ^8.0.0-0 + + babel-loader@8.3.0: + resolution: {integrity: sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==} + engines: {node: '>= 8.9'} + peerDependencies: + '@babel/core': ^7.0.0 + webpack: '>=2' + + babel-messages@6.23.0: + resolution: {integrity: sha512-Bl3ZiA+LjqaMtNYopA9TYE9HP1tQ+E5dLxE0XrAzcIJeK2UqF0/EaqXwBn9esd4UmTfEab+P+UYQ1GnioFIb/w==} + + babel-plugin-istanbul@7.0.1: + resolution: {integrity: sha512-D8Z6Qm8jCvVXtIRkBnqNHX0zJ37rQcFJ9u8WOS6tkYOsRdHBzypCstaxWiu5ZIlqQtviRYbgnRLSoCEvjqcqbA==} + engines: {node: '>=12'} + + babel-plugin-jest-hoist@30.2.0: + resolution: {integrity: sha512-ftzhzSGMUnOzcCXd6WHdBGMyuwy15Wnn0iyyWGKgBDLxf9/s5ABuraCSpBX2uG0jUg4rqJnxsLc5+oYBqoxVaA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + babel-plugin-polyfill-corejs2@0.4.11: + resolution: {integrity: sha512-sMEJ27L0gRHShOh5G54uAAPaiCOygY/5ratXuiyb2G46FmlSpc9eFCzYVyDiPxfNbwzA7mYahmjQc5q+CZQ09Q==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-corejs3@0.10.4: + resolution: {integrity: sha512-25J6I8NGfa5YkCDogHRID3fVCadIR8/pGl1/spvCkzb6lVn6SR3ojpx9nOn9iEBcUsjY24AmdKm5khcfKdylcg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-polyfill-regenerator@0.6.2: + resolution: {integrity: sha512-2R25rQZWP63nGwaAswvDazbPXfrM3HwVoBXK6HcqeKrSrL/JqcC/rDcf95l4r7LXLyxDXc8uQDa064GubtCABg==} + peerDependencies: + '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-transform-es2015-modules-commonjs@6.26.2: + resolution: {integrity: sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==} + + babel-plugin-transform-strict-mode@6.24.1: + resolution: {integrity: sha512-j3KtSpjyLSJxNoCDrhwiJad8kw0gJ9REGj8/CqL0HeRyLnvUNYV9zcqluL6QJSXh3nfsLEmSLvwRfGzrgR96Pw==} + + babel-preset-current-node-syntax@1.2.0: + resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==} + peerDependencies: + '@babel/core': ^7.0.0 || ^8.0.0-0 + + babel-preset-jest@30.2.0: + resolution: {integrity: sha512-US4Z3NOieAQumwFnYdUWKvUKh8+YSnS/gB3t6YBiz0bskpu7Pine8pPCheNxlPEW4wnUkma2a94YuW2q3guvCQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + '@babel/core': ^7.11.0 || ^8.0.0-beta.1 + + babel-runtime@6.26.0: + resolution: {integrity: sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==} + + babel-template@6.26.0: + resolution: {integrity: sha512-PCOcLFW7/eazGUKIoqH97sO9A2UYMahsn/yRQ7uOk37iutwjq7ODtcTNF+iFDSHNfkctqsLRjLP7URnOx0T1fg==} + + babel-traverse@6.26.0: + resolution: {integrity: sha512-iSxeXx7apsjCHe9c7n8VtRXGzI2Bk1rBSOJgCCjfyXb6v1aCqE1KSEpq/8SXuVN8Ka/Rh1WDTF0MDzkvTA4MIA==} + + babel-types@6.26.0: + resolution: {integrity: sha512-zhe3V/26rCWsEZK8kZN+HaQj5yQ1CilTObixFzKW1UWjqG7618Twz6YEsCnjfg5gBcJh02DrpCkS9h98ZqDY+g==} + + babylon@6.18.0: + resolution: {integrity: sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==} + hasBin: true + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@2.0.0: + resolution: {integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + base@0.11.2: + resolution: {integrity: sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==} + engines: {node: '>=0.10.0'} + + baseline-browser-mapping@2.9.11: + resolution: {integrity: sha512-Sg0xJUNDU1sJNGdfGWhVHX0kkZ+HWcvmVymJbj6NSgZZmW/8S9Y2HQ5euytnIgakgxN6papOAWiwDo1ctFDcoQ==} + hasBin: true + + big.js@5.2.2: + resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==} + + bignumber.js@9.1.2: + resolution: {integrity: sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==} + + binary-extensions@1.13.1: + resolution: {integrity: sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==} + engines: {node: '>=0.10.0'} + + binary-extensions@2.2.0: + resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==} + engines: {node: '>=8'} + + bindings@1.5.0: + resolution: {integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==} + + bluebird@3.7.2: + resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} + + bn.js@4.12.0: + resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==} + + bn.js@5.2.1: + resolution: {integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + boxen@5.1.2: + resolution: {integrity: sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==} + engines: {node: '>=10'} + + brace-expansion@1.1.11: + resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + brace-expansion@2.1.0: + resolution: {integrity: sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==} + + braces@2.3.2: + resolution: {integrity: sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==} + engines: {node: '>=0.10.0'} + + braces@3.0.2: + resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==} + engines: {node: '>=8'} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + brorand@1.1.0: + resolution: {integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==} + + browserify-aes@1.2.0: + resolution: {integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==} + + browserify-cipher@1.0.1: + resolution: {integrity: sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==} + + browserify-des@1.0.2: + resolution: {integrity: sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==} + + browserify-rsa@4.1.0: + resolution: {integrity: sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==} + + browserify-sign@4.2.2: + resolution: {integrity: sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg==} + engines: {node: '>= 4'} + + browserify-zlib@0.2.0: + resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} + + browserslist@4.28.1: + resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + bs-logger@0.2.6: + resolution: {integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==} + engines: {node: '>= 6'} + + bser@2.1.1: + resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==} + + buffer-equal-constant-time@1.0.1: + resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} + + buffer-from@1.1.2: + resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} + + buffer-json@2.0.0: + resolution: {integrity: sha512-+jjPFVqyfF1esi9fvfUs3NqM0pH1ziZ36VP4hmA/y/Ssfo/5w5xHKfTw9BwQjoJ1w/oVtpLomqwUHKdefGyuHw==} + + buffer-xor@1.0.3: + resolution: {integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==} + + buffer@4.9.2: + resolution: {integrity: sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==} + + builtin-modules@3.3.0: + resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==} + engines: {node: '>=6'} + + builtin-status-codes@3.0.0: + resolution: {integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==} + + builtins@5.0.1: + resolution: {integrity: sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==} + + bytes@3.0.0: + resolution: {integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==} + engines: {node: '>= 0.8'} + + c12@1.11.1: + resolution: {integrity: sha512-KDU0TvSvVdaYcQKQ6iPHATGz/7p/KiVjPg4vQrB6Jg/wX9R0yl5RZxWm9IoZqaIHD2+6PZd81+KMGwRr/lRIUg==} + peerDependencies: + magicast: ^0.3.4 + peerDependenciesMeta: + magicast: + optional: true + + c12@1.4.2: + resolution: {integrity: sha512-3IP/MuamSVRVw8W8+CHWAz9gKN4gd+voF2zm/Ln6D25C2RhytEZ1ABbC8MjKr4BR9rhoV1JQ7jJA158LDiTkLg==} + + cacache@12.0.4: + resolution: {integrity: sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==} + + cacache@15.3.0: + resolution: {integrity: sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==} + engines: {node: '>= 10'} + + cache-base@1.0.1: + resolution: {integrity: sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==} + engines: {node: '>=0.10.0'} + + cache-loader@4.1.0: + resolution: {integrity: sha512-ftOayxve0PwKzBF/GLsZNC9fJBXl8lkZE3TOsjkboHfVHVkL39iUEs1FO07A33mizmci5Dudt38UZrrYXDtbhw==} + engines: {node: '>= 8.9.0'} + peerDependencies: + webpack: ^4.0.0 + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.2: + resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==} + + callsite@1.0.0: + resolution: {integrity: sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camel-case@4.1.2: + resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} + + camelcase-keys@7.0.2: + resolution: {integrity: sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==} + engines: {node: '>=12'} + + camelcase@5.3.1: + resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} + engines: {node: '>=6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + caniuse-api@3.0.0: + resolution: {integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==} + + caniuse-lite@1.0.30001639: + resolution: {integrity: sha512-eFHflNTBIlFwP2AIKaYuBQN/apnUoKNhBdza8ZnW/h2di4LCZ4xFqYlxUxo+LQ76KFI1PGcC1QDxMbxTZpSCAg==} + + caniuse-lite@1.0.30001762: + resolution: {integrity: sha512-PxZwGNvH7Ak8WX5iXzoK1KPZttBXNPuaOvI2ZYU7NrlM+d9Ov+TUvlLOBNGzVXAntMSMMlJPd+jY6ovrVjSmUw==} + + chalk@1.1.3: + resolution: {integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==} + engines: {node: '>=0.10.0'} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.5.0: + resolution: {integrity: sha512-1tm8DTaJhPBG3bIkVeZt1iZM9GfSX2lzOeDVZH9R9ffRHpmHvxZ/QhgQH/aDTkswQVt+YHdXAdS/In/30OjCbg==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + char-regex@1.0.2: + resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} + engines: {node: '>=10'} + + chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + + chart.js@4.5.1: + resolution: {integrity: sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==} + engines: {pnpm: '>=8'} + + chartjs-adapter-moment@1.0.1: + resolution: {integrity: sha512-Uz+nTX/GxocuqXpGylxK19YG4R3OSVf8326D+HwSTsNw1LgzyIGRo+Qujwro1wy6X+soNSnfj5t2vZ+r6EaDmA==} + peerDependencies: + chart.js: '>=3.0.0' + moment: ^2.10.2 + + chokidar@2.1.8: + resolution: {integrity: sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==} + deprecated: Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies + + chokidar@3.5.3: + resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} + engines: {node: '>= 8.10.0'} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + + chownr@2.0.0: + resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} + engines: {node: '>=10'} + + chrome-trace-event@1.0.4: + resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} + engines: {node: '>=6.0'} + + ci-info@3.8.0: + resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} + engines: {node: '>=8'} + + ci-info@3.9.0: + resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} + engines: {node: '>=8'} + + ci-info@4.3.0: + resolution: {integrity: sha512-l+2bNRMiQgcfILUi33labAZYIWlH1kWDp+ecNo5iisRKrbm0xcRyCww71/YU0Fkw0mAFpz9bJayXPjey6vkmaQ==} + engines: {node: '>=8'} + + cipher-base@1.0.4: + resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==} + + citty@0.1.6: + resolution: {integrity: sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==} + + cjs-module-lexer@2.1.0: + resolution: {integrity: sha512-UX0OwmYRYQQetfrLEZeewIFFI+wSTofC+pMBLNuH3RUuu/xzG1oz84UCEDOSoQlN3fZ4+AzmV50ZYvGqkMh9yA==} + + class-utils@0.3.6: + resolution: {integrity: sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==} + engines: {node: '>=0.10.0'} + + clean-css@4.2.4: + resolution: {integrity: sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==} + engines: {node: '>= 4.0'} + + clean-css@5.3.3: + resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} + engines: {node: '>= 10.0'} + + clean-regexp@1.0.0: + resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==} + engines: {node: '>=4'} + + clean-stack@2.2.0: + resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} + engines: {node: '>=6'} + + cli-boxes@2.2.1: + resolution: {integrity: sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==} + engines: {node: '>=6'} + + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-truncate@4.0.0: + resolution: {integrity: sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA==} + engines: {node: '>=18'} + + cli-width@3.0.0: + resolution: {integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==} + engines: {node: '>= 10'} + + cliui@6.0.0: + resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==} + + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + clone@2.1.2: + resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} + engines: {node: '>=0.8'} + + co@4.6.0: + resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} + engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} + + collect-v8-coverage@1.0.2: + resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} + + collection-visit@1.0.0: + resolution: {integrity: sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==} + engines: {node: '>=0.10.0'} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + colord@2.9.3: + resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==} + + colorette@2.0.20: + resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + commander@10.0.1: + resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==} + engines: {node: '>=14'} + + commander@14.0.0: + resolution: {integrity: sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==} + engines: {node: '>=20'} + + commander@2.20.3: + resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + commondir@1.0.1: + resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==} + + compare-func@2.0.0: + resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} + + compatx@0.1.8: + resolution: {integrity: sha512-jcbsEAR81Bt5s1qOFymBufmCbXCXbk0Ql+K5ouj6gCyx2yHlu6AgmGIi9HxfKixpUDO5bCFJUHQ5uM6ecbTebw==} + + component-emitter@1.3.0: + resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==} + + compressible@2.0.18: + resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} + engines: {node: '>= 0.6'} + + compression@1.7.4: + resolution: {integrity: sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==} + engines: {node: '>= 0.8.0'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + concat-stream@1.6.2: + resolution: {integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==} + engines: {'0': node >= 0.8} + + condense-newlines@0.2.1: + resolution: {integrity: sha512-P7X+QL9Hb9B/c8HI5BFFKmjgBu2XpQuF98WZ9XkO+dBGgk5XgwiQz7o1SmpglNWId3581UcS0SFAWfoIhMHPfg==} + engines: {node: '>=0.10.0'} + + confbox@0.1.7: + resolution: {integrity: sha512-uJcB/FKZtBMCJpK8MQji6bJHgu1tixKPxRLeGkNzBoOZzpnZUJm0jm2/sBDWcuBx1dYgxV4JU+g5hmNxCyAmdA==} + + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + + configstore@5.0.1: + resolution: {integrity: sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==} + engines: {node: '>=8'} + + connect@3.7.0: + resolution: {integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==} + engines: {node: '>= 0.10.0'} + + consola@2.15.3: + resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} + + consola@3.2.3: + resolution: {integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==} + engines: {node: ^14.18.0 || >=16.10.0} + + console-browserify@1.2.0: + resolution: {integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==} + + consolidate@0.15.1: + resolution: {integrity: sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==} + engines: {node: '>= 0.10.0'} + deprecated: Please upgrade to consolidate v1.0.0+ as it has been modernized with several long-awaited fixes implemented. Maintenance is supported by Forward Email at https://forwardemail.net ; follow/watch https://github.com/ladjs/consolidate for updates and release changelog + peerDependencies: + arc-templates: ^0.5.3 + atpl: '>=0.7.6' + babel-core: ^6.26.3 + bracket-template: ^1.1.5 + coffee-script: ^1.12.7 + dot: ^1.1.3 + dust: ^0.3.0 + dustjs-helpers: ^1.7.4 + dustjs-linkedin: ^2.7.5 + eco: ^1.1.0-rc-3 + ect: ^0.5.9 + ejs: ^3.1.5 + haml-coffee: ^1.14.1 + hamlet: ^0.3.3 + hamljs: ^0.6.2 + handlebars: ^4.7.6 + hogan.js: ^3.0.2 + htmling: ^0.0.8 + jade: ^1.11.0 + jazz: ^0.0.18 + jqtpl: ~1.1.0 + just: ^0.1.8 + liquid-node: ^3.0.1 + liquor: ^0.0.5 + lodash: ^4.17.20 + marko: ^3.14.4 + mote: ^0.2.0 + mustache: ^3.0.0 + nunjucks: ^3.2.2 + plates: ~0.4.11 + pug: ^3.0.0 + qejs: ^3.0.5 + ractive: ^1.3.12 + razor-tmpl: ^1.3.1 + react: ^16.13.1 + react-dom: ^16.13.1 + slm: ^2.0.0 + squirrelly: ^5.1.0 + swig: ^1.4.2 + swig-templates: ^2.0.3 + teacup: ^2.0.0 + templayed: '>=0.2.3' + then-jade: '*' + then-pug: '*' + tinyliquid: ^0.2.34 + toffee: ^0.3.6 + twig: ^1.15.2 + twing: ^5.0.2 + underscore: ^1.11.0 + vash: ^0.13.0 + velocityjs: ^2.0.1 + walrus: ^0.10.1 + whiskers: ^0.4.0 + peerDependenciesMeta: + arc-templates: + optional: true + atpl: + optional: true + babel-core: + optional: true + bracket-template: + optional: true + coffee-script: + optional: true + dot: + optional: true + dust: + optional: true + dustjs-helpers: + optional: true + dustjs-linkedin: + optional: true + eco: + optional: true + ect: + optional: true + ejs: + optional: true + haml-coffee: + optional: true + hamlet: + optional: true + hamljs: + optional: true + handlebars: + optional: true + hogan.js: + optional: true + htmling: + optional: true + jade: + optional: true + jazz: + optional: true + jqtpl: + optional: true + just: + optional: true + liquid-node: + optional: true + liquor: + optional: true + lodash: + optional: true + marko: + optional: true + mote: + optional: true + mustache: + optional: true + nunjucks: + optional: true + plates: + optional: true + pug: + optional: true + qejs: + optional: true + ractive: + optional: true + razor-tmpl: + optional: true + react: + optional: true + react-dom: + optional: true + slm: + optional: true + squirrelly: + optional: true + swig: + optional: true + swig-templates: + optional: true + teacup: + optional: true + templayed: + optional: true + then-jade: + optional: true + then-pug: + optional: true + tinyliquid: + optional: true + toffee: + optional: true + twig: + optional: true + twing: + optional: true + underscore: + optional: true + vash: + optional: true + velocityjs: + optional: true + walrus: + optional: true + whiskers: + optional: true + + constants-browserify@1.0.0: + resolution: {integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==} + + conventional-changelog-angular@8.1.0: + resolution: {integrity: sha512-GGf2Nipn1RUCAktxuVauVr1e3r8QrLP/B0lEUsFktmGqc3ddbQkhoJZHJctVU829U1c6mTSWftrVOCHaL85Q3w==} + engines: {node: '>=18'} + + conventional-changelog-conventionalcommits@9.1.0: + resolution: {integrity: sha512-MnbEysR8wWa8dAEvbj5xcBgJKQlX/m0lhS8DsyAAWDHdfs2faDJxTgzRYlRYpXSe7UiKrIIlB4TrBKU9q9DgkA==} + engines: {node: '>=18'} + + conventional-commits-parser@6.2.1: + resolution: {integrity: sha512-20pyHgnO40rvfI0NGF/xiEoFMkXDtkF8FwHvk5BokoFoCuTQRI8vrNCNFWUOfuolKJMm1tPCHc8GgYEtr1XRNA==} + engines: {node: '>=18'} + hasBin: true + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cookie@0.3.1: + resolution: {integrity: sha512-+IJOX0OqlHCszo2mBUq+SrEbCj6w7Kpffqx60zYbPTFaO4+yYgRjHwcZNpWvaTylDHaV7PPmBHzSecZiMhtPgw==} + engines: {node: '>= 0.6'} + + copy-concurrently@1.0.5: + resolution: {integrity: sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==} + deprecated: This package is no longer supported. + + copy-descriptor@0.1.1: + resolution: {integrity: sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==} + engines: {node: '>=0.10.0'} + + core-js-compat@3.37.1: + resolution: {integrity: sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==} + + core-js@2.6.12: + resolution: {integrity: sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==} + deprecated: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js. + + core-js@3.48.0: + resolution: {integrity: sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cosmiconfig-typescript-loader@6.2.0: + resolution: {integrity: sha512-GEN39v7TgdxgIoNcdkRE3uiAzQt3UXLyHbRHD6YoL048XAeOomyxaP+Hh/+2C6C2wYjxJ2onhJcsQp+L4YEkVQ==} + engines: {node: '>=v18'} + peerDependencies: + '@types/node': '*' + cosmiconfig: '>=9' + typescript: '>=5' + + cosmiconfig@6.0.0: + resolution: {integrity: sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==} + engines: {node: '>=8'} + + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + + cosmiconfig@8.3.6: + resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + cosmiconfig@9.0.0: + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + crc@4.3.2: + resolution: {integrity: sha512-uGDHf4KLLh2zsHa8D8hIQ1H/HtFQhyHrc0uhHBcoKGol/Xnb+MPYfUMw7cvON6ze/GUESTudKayDcJC5HnJv1A==} + engines: {node: '>=12'} + peerDependencies: + buffer: '>=6.0.3' + peerDependenciesMeta: + buffer: + optional: true + + create-ecdh@4.0.4: + resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==} + + create-hash@1.2.0: + resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} + + create-hmac@1.1.7: + resolution: {integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==} + + create-require@1.1.1: + resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + crypto-browserify@3.12.0: + resolution: {integrity: sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==} + + crypto-random-string@2.0.0: + resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} + engines: {node: '>=8'} + + css-blank-pseudo@6.0.2: + resolution: {integrity: sha512-J/6m+lsqpKPqWHOifAFtKFeGLOzw3jR92rxQcwRUfA/eTuZzKfKlxOmYDx2+tqOPQAueNvBiY8WhAeHu5qNmTg==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + + css-declaration-sorter@6.4.1: + resolution: {integrity: sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==} + engines: {node: ^10 || ^12 || >=14} + peerDependencies: + postcss: ^8.0.9 + + css-declaration-sorter@7.2.0: + resolution: {integrity: sha512-h70rUM+3PNFuaBDTLe8wF/cdWu+dOZmb7pJt8Z2sedYbAcQVQV/tEchueg3GWxwqS0cxtbxmaHEdkNACqcvsow==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.0.9 + + css-functions-list@3.2.1: + resolution: {integrity: sha512-Nj5YcaGgBtuUmn1D7oHqPW0c9iui7xsTsj5lIX8ZgevdfhmjFfKB3r8moHJtNJnctnYXJyYX5I1pp90HM4TPgQ==} + engines: {node: '>=12 || >=16'} + + css-has-pseudo@6.0.5: + resolution: {integrity: sha512-ZTv6RlvJJZKp32jPYnAJVhowDCrRrHUTAxsYSuUPBEDJjzws6neMnzkRblxtgmv1RgcV5dhH2gn7E3wA9Wt6lw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + + css-loader@5.2.7: + resolution: {integrity: sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: ^4.27.0 || ^5.0.0 + + css-prefers-color-scheme@9.0.1: + resolution: {integrity: sha512-iFit06ochwCKPRiWagbTa1OAWCvWWVdEnIFd8BaRrgO8YrrNh4RAWUQTFcYX5tdFZgFl1DJ3iiULchZyEbnF4g==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + + css-select@4.3.0: + resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} + + css-select@5.1.0: + resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + + css-tree@1.1.3: + resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} + engines: {node: '>=8.0.0'} + + css-tree@2.2.1: + resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + css-tree@2.3.1: + resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css-what@6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + + css@2.2.4: + resolution: {integrity: sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==} + + cssdb@8.0.2: + resolution: {integrity: sha512-zbOCmmbcHvr2lP+XrZSgftGMGumbosC6IM3dbxwifwPEBD70pVJaH3Ho191VBEqDg644AM7PPPVj0ZXokTjZng==} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + cssnano-preset-default@5.2.14: + resolution: {integrity: sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + cssnano-preset-default@7.0.3: + resolution: {integrity: sha512-dQ3Ba1p/oewICp/szF1XjFFgql8OlOBrI2YNBUUwhHQnJNoMOcQTa+Bi7jSJN8r/eM1egW0Ud1se/S7qlduWKA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + cssnano-utils@3.1.0: + resolution: {integrity: sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + cssnano-utils@5.0.0: + resolution: {integrity: sha512-Uij0Xdxc24L6SirFr25MlwC2rCFX6scyUmuKpzI+JQ7cyqDEwD42fJ0xfB3yLfOnRDU5LKGgjQ9FA6LYh76GWQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + cssnano@5.1.15: + resolution: {integrity: sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + cssnano@7.0.3: + resolution: {integrity: sha512-lsekJctOTqdCn4cNrtrSwsuMR/fHC+oiVMHkp/OugBWtwjH8XJag1/OtGaYJGtz0un1fQcRy4ryfYTQsfh+KSQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + csso@4.2.0: + resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==} + engines: {node: '>=8.0.0'} + + csso@5.0.5: + resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'} + + cssstyle@4.6.0: + resolution: {integrity: sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==} + engines: {node: '>=18'} + + csstype@3.1.2: + resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} + + cuint@0.2.2: + resolution: {integrity: sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw==} + + cyclist@1.0.2: + resolution: {integrity: sha512-0sVXIohTfLqVIW3kb/0n6IiWF3Ifj5nm2XaSrLq2DI6fKIGa2fYAZdk917rUneaeLVpYfFcyXE2ft0fe3remsA==} + + dargs@8.1.0: + resolution: {integrity: sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==} + engines: {node: '>=12'} + + data-urls@5.0.0: + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} + + date-fns@2.30.0: + resolution: {integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==} + engines: {node: '>=0.11'} + + de-indent@1.0.2: + resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==} + + deasync@0.1.29: + resolution: {integrity: sha512-EBtfUhVX23CE9GR6m+F8WPeImEE4hR/FW9RkK0PMl9V1t283s0elqsTD8EZjaKX28SY1BW2rYfCgNsAYdpamUw==} + engines: {node: '>=0.11.0'} + + debounce@1.2.1: + resolution: {integrity: sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug==} + + debug@2.6.9: + resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.3.4: + resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.3.6: + resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decache@4.6.2: + resolution: {integrity: sha512-2LPqkLeu8XWHU8qNCS3kcF6sCcb5zIzvWaAHYSvPfwhdd7mHuah29NssMzrTYyHN4F5oFy2ko9OBYxegtU0FEw==} + + decamelize-keys@1.1.1: + resolution: {integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==} + engines: {node: '>=0.10.0'} + + decamelize@1.2.0: + resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} + engines: {node: '>=0.10.0'} + + decamelize@5.0.1: + resolution: {integrity: sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==} + engines: {node: '>=10'} + + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + + decode-uri-component@0.2.2: + resolution: {integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==} + engines: {node: '>=0.10'} + + dedent@1.7.0: + resolution: {integrity: sha512-HGFtf8yhuhGhqO07SV79tRp+br4MnbdjeVxotpn1QBl30pcLLCQjX5b2295ll0fv8RKDKsmWYrl05usHM9CewQ==} + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + define-data-property@1.1.0: + resolution: {integrity: sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + define-property@0.2.5: + resolution: {integrity: sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==} + engines: {node: '>=0.10.0'} + + define-property@1.0.0: + resolution: {integrity: sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==} + engines: {node: '>=0.10.0'} + + define-property@2.0.2: + resolution: {integrity: sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==} + engines: {node: '>=0.10.0'} + + defu@5.0.1: + resolution: {integrity: sha512-EPS1carKg+dkEVy3qNTqIdp2qV7mUP08nIsupfwQpz++slCVRw7qbQyWvSTig+kFPwz2XXp5/kIIkH+CwrJKkQ==} + + defu@6.1.2: + resolution: {integrity: sha512-+uO4+qr7msjNNWKYPHqN/3+Dx3NFkmIzayk2L1MyZQlvgZb/J1A0fo410dpKrN2SnqFjt8n4JL8fDJE0wIgjFQ==} + + defu@6.1.4: + resolution: {integrity: sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + depd@2.0.0: + resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} + engines: {node: '>= 0.8'} + + des.js@1.1.0: + resolution: {integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==} + + destr@2.0.3: + resolution: {integrity: sha512-2N3BOUU4gYMpTP24s5rF5iP7BDr7uNTCs4ozw3kf/eKfvWSIu93GEBi5m427YoyJoeOzQ5smuu4nNAPGb8idSQ==} + + destroy@1.2.0: + resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} + engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} + + detect-indent@5.0.0: + resolution: {integrity: sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g==} + engines: {node: '>=4'} + + detect-newline@3.1.0: + resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} + engines: {node: '>=8'} + + devalue@2.0.1: + resolution: {integrity: sha512-I2TiqT5iWBEyB8GRfTDP0hiLZ0YeDJZ+upDxjBfOC2lebO5LezQMv7QvIUTzdb64jQyAKLf1AHADtGN+jw6v8Q==} + + dialog-polyfill@0.4.10: + resolution: {integrity: sha512-j5yGMkP8T00UFgyO+78OxiN5vC5dzRQF3BEio+LhNvDbyfxWBsi3sfPArDm54VloaJwy2hm3erEiDWqHRC8rzw==} + + diffie-hellman@5.0.3: + resolution: {integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==} + + dijkstrajs@1.0.3: + resolution: {integrity: sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + doctrine@3.0.0: + resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} + engines: {node: '>=6.0.0'} + + dom-converter@0.2.0: + resolution: {integrity: sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==} + + dom-event-types@1.1.0: + resolution: {integrity: sha512-jNCX+uNJ3v38BKvPbpki6j5ItVlnSqVV6vDWGS6rExzCMjsc39frLjm1n91o6YaKK6AZl0wLloItW6C6mr61BQ==} + + dom-serializer@1.4.1: + resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} + + dom-serializer@2.0.0: + resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==} + + domain-browser@1.2.0: + resolution: {integrity: sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==} + engines: {node: '>=0.4', npm: '>=1.2'} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@4.3.1: + resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} + engines: {node: '>= 4'} + + domhandler@5.0.3: + resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==} + engines: {node: '>= 4'} + + domutils@2.8.0: + resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} + + domutils@3.2.2: + resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==} + + dot-case@3.0.4: + resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} + + dot-prop@5.3.0: + resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} + engines: {node: '>=8'} + + dotenv@16.6.1: + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} + engines: {node: '>=12'} + + dotenv@17.2.3: + resolution: {integrity: sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==} + engines: {node: '>=12'} + + dotenv@8.6.0: + resolution: {integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==} + engines: {node: '>=10'} + + dotenv@9.0.2: + resolution: {integrity: sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==} + engines: {node: '>=10'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + duplexer@0.1.2: + resolution: {integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==} + + duplexify@3.7.1: + resolution: {integrity: sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==} + + duplexify@4.1.2: + resolution: {integrity: sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + ecdsa-sig-formatter@1.0.11: + resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==} + + editorconfig@1.0.4: + resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==} + engines: {node: '>=14'} + hasBin: true + + ee-first@1.1.1: + resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} + + ejs@3.1.10: + resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} + engines: {node: '>=0.10.0'} + hasBin: true + + electron-to-chromium@1.5.267: + resolution: {integrity: sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==} + + elliptic@6.6.0: + resolution: {integrity: sha512-dpwoQcLc/2WLQvJvLRHKZ+f9FgOdjnq11rurqwekGQygGPsYSK29OMMD2WalatiqQ+XGFDglTNixpPfI+lpaAA==} + + emittery@0.13.1: + resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} + engines: {node: '>=12'} + + emoji-regex@10.4.0: + resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + emojis-list@3.0.0: + resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==} + engines: {node: '>= 4'} + + encodeurl@1.0.2: + resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} + engines: {node: '>= 0.8'} + + encodeurl@2.0.0: + resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} + engines: {node: '>= 0.8'} + + end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + + enhanced-resolve@4.5.0: + resolution: {integrity: sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==} + engines: {node: '>=6.9.0'} + + enhanced-resolve@5.18.4: + resolution: {integrity: sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==} + engines: {node: '>=10.13.0'} + + ent@2.2.0: + resolution: {integrity: sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==} + + entities@2.2.0: + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + entities@6.0.1: + resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==} + engines: {node: '>=0.12'} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + environment@1.1.0: + resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} + engines: {node: '>=18'} + + errno@0.1.8: + resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} + hasBin: true + + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + + error-stack-parser@2.1.4: + resolution: {integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==} + + es-abstract@1.22.2: + resolution: {integrity: sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA==} + engines: {node: '>= 0.4'} + + es-array-method-boxes-properly@1.0.0: + resolution: {integrity: sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@2.0.0: + resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.0.0: + resolution: {integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==} + + es-to-primitive@1.2.1: + resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} + engines: {node: '>= 0.4'} + + esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-html@1.0.3: + resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@2.0.0: + resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} + engines: {node: '>=8'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + eslint-config-prettier@10.1.8: + resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-config-standard@17.1.0: + resolution: {integrity: sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==} + engines: {node: '>=12.0.0'} + peerDependencies: + eslint: ^8.0.1 + eslint-plugin-import: ^2.25.2 + eslint-plugin-n: '^15.0.0 || ^16.0.0 ' + eslint-plugin-promise: ^6.0.0 + + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-import-resolver-typescript@3.6.1: + resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + + eslint-module-utils@2.8.0: + resolution: {integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-es@3.0.1: + resolution: {integrity: sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==} + engines: {node: '>=8.10.0'} + peerDependencies: + eslint: '>=4.19.1' + + eslint-plugin-es@4.1.0: + resolution: {integrity: sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==} + engines: {node: '>=8.10.0'} + peerDependencies: + eslint: '>=4.19.1' + + eslint-plugin-import@2.28.1: + resolution: {integrity: sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-n@15.7.0: + resolution: {integrity: sha512-jDex9s7D/Qial8AGVIHq4W7NswpUD5DPDL2RH8Lzd9EloWUuvUkHfv4FRLMipH5q2UtyurorBkPeNi1wVWNh3Q==} + engines: {node: '>=12.22.0'} + peerDependencies: + eslint: '>=7.0.0' + + eslint-plugin-node@11.1.0: + resolution: {integrity: sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==} + engines: {node: '>=8.10.0'} + peerDependencies: + eslint: '>=5.16.0' + + eslint-plugin-nuxt@4.0.0: + resolution: {integrity: sha512-v3Vwdk8YKe52bAz8eSIDqQuTtfL/T1r9dSl1uhC5SyR5pgLxgKkQdxXVf/Bf6Ax7uyd9rHqiAuYVdqqDb7ILdA==} + + eslint-plugin-promise@6.1.1: + resolution: {integrity: sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^7.0.0 || ^8.0.0 + + eslint-plugin-unicorn@44.0.2: + resolution: {integrity: sha512-GLIDX1wmeEqpGaKcnMcqRvMVsoabeF0Ton0EX4Th5u6Kmf7RM9WBl705AXFEsns56ESkEs0uyelLuUTvz9Tr0w==} + engines: {node: '>=14.18'} + peerDependencies: + eslint: '>=8.23.1' + + eslint-plugin-vue@9.33.0: + resolution: {integrity: sha512-174lJKuNsuDIlLpjeXc5E2Tss8P44uIimAfGD0b90k0NoirJqpG7stLuU9Vp/9ioTOrQdWVREc4mRd1BD+CvGw==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 || ^9.0.0 + + eslint-scope@4.0.3: + resolution: {integrity: sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==} + engines: {node: '>=4.0.0'} + + eslint-scope@5.1.1: + resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} + engines: {node: '>=8.0.0'} + + eslint-scope@7.2.2: + resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-utils@2.1.0: + resolution: {integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==} + engines: {node: '>=6'} + + eslint-utils@3.0.0: + resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==} + engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0} + peerDependencies: + eslint: '>=5' + + eslint-visitor-keys@1.3.0: + resolution: {integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==} + engines: {node: '>=4'} + + eslint-visitor-keys@2.1.0: + resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==} + engines: {node: '>=10'} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-webpack-plugin@4.0.1: + resolution: {integrity: sha512-fUFcXpui/FftGx3NzvWgLZXlLbu+m74sUxGEgxgoxYcUtkIQbS6SdNNZkS99m5ycb23TfoNYrDpp1k/CK5j6Hw==} + engines: {node: '>= 14.15.0'} + peerDependencies: + eslint: ^8.0.0 + webpack: ^5.0.0 + + eslint@8.57.1: + resolution: {integrity: sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + deprecated: This version is no longer supported. Please see https://eslint.org/version-support for other options. + hasBin: true + + espree@9.6.1: + resolution: {integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + esprima@4.0.1: + resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} + engines: {node: '>=4'} + hasBin: true + + esquery@1.5.0: + resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==} + engines: {node: '>=0.10'} + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@4.3.0: + resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + etag@1.8.1: + resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} + engines: {node: '>= 0.6'} + + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + eventsource-polyfill@0.9.6: + resolution: {integrity: sha512-LyMFp2oPDGhum2lMvkjqKZEwWd2/AoXyt8aoyftTBMWwPHNgU+2tdxhTHPluDxoz+z4gNj0uHAPR9nqevATMbg==} + + evp_bytestokey@1.0.3: + resolution: {integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==} + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + execa@8.0.1: + resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} + engines: {node: '>=16.17'} + + exit-x@0.2.2: + resolution: {integrity: sha512-+I6B/IkJc1o/2tiURyz/ivu/O0nKNEArIUB5O7zBrlDVJr22SCLH3xTeEry428LvFhRzIA1g8izguxJ/gbNcVQ==} + engines: {node: '>= 0.8.0'} + + exit@0.1.2: + resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==} + engines: {node: '>= 0.8.0'} + + expand-brackets@2.1.4: + resolution: {integrity: sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==} + engines: {node: '>=0.10.0'} + + expect@30.2.0: + resolution: {integrity: sha512-u/feCi0GPsI+988gU2FLcsHyAHTU0MX1Wg68NhAnN7z/+C5wqG+CY8J53N9ioe8RXgaoz0nBR/TYMf3AycUuPw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + extend-shallow@2.0.1: + resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==} + engines: {node: '>=0.10.0'} + + extend-shallow@3.0.2: + resolution: {integrity: sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==} + engines: {node: '>=0.10.0'} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + + extglob@2.0.4: + resolution: {integrity: sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==} + engines: {node: '>=0.10.0'} + + extract-css-chunks-webpack-plugin@4.10.0: + resolution: {integrity: sha512-D/wb/Tbexq8XMBl4uhthto25WBaHI9P8vucDdzwPtLTyVi4Rdw/aiRLSL2rHaF6jZfPAjThWXepFU9PXsdtIbA==} + engines: {node: '>= 6.9.0'} + peerDependencies: + webpack: ^4.4.0 || ^5.0.0 + + extract-from-css@0.4.4: + resolution: {integrity: sha512-41qWGBdtKp9U7sgBxAQ7vonYqSXzgW/SiAYzq4tdWSVhAShvpVCH1nyvPQgjse6EdgbW7Y7ERdT3674/lKr65A==} + engines: {node: '>=0.10.0', npm: '>=2.0.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} + engines: {node: '>=8.6.0'} + + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fast-text-encoding@1.0.6: + resolution: {integrity: sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w==} + + fast-uri@3.1.0: + resolution: {integrity: sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==} + + fastest-levenshtein@1.0.16: + resolution: {integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==} + engines: {node: '>= 4.9.1'} + + fastq@1.15.0: + resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==} + + faye-websocket@0.11.4: + resolution: {integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==} + engines: {node: '>=0.8.0'} + + fb-watchman@2.0.2: + resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} + + figgy-pudding@3.5.2: + resolution: {integrity: sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==} + deprecated: This module is no longer supported. + + figures@3.2.0: + resolution: {integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==} + engines: {node: '>=8'} + + file-entry-cache@6.0.1: + resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==} + engines: {node: ^10.12.0 || >=12.0.0} + + file-entry-cache@7.0.1: + resolution: {integrity: sha512-uLfFktPmRetVCbHe5UPuekWrQ6hENufnA46qEGbfACkK5drjTTdQYUragRgMjHldcbYG+nslUerqMPjbBSHXjQ==} + engines: {node: '>=12.0.0'} + + file-loader@6.2.0: + resolution: {integrity: sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + + file-uri-to-path@1.0.0: + resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} + + filelist@1.0.6: + resolution: {integrity: sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==} + + fill-range@4.0.0: + resolution: {integrity: sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==} + engines: {node: '>=0.10.0'} + + fill-range@7.0.1: + resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==} + engines: {node: '>=8'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + finalhandler@1.1.2: + resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==} + engines: {node: '>= 0.8'} + + find-babel-config@1.2.0: + resolution: {integrity: sha512-jB2CHJeqy6a820ssiqwrKMeyC6nNdmrcgkKWJWmpoxpE8RKciYJXCcXRq1h2AzCo5I5BJeN2tkGEO3hLTuePRA==} + engines: {node: '>=4.0.0'} + + find-cache-dir@2.1.0: + resolution: {integrity: sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==} + engines: {node: '>=6'} + + find-cache-dir@3.3.2: + resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==} + engines: {node: '>=8'} + + find-up@3.0.0: + resolution: {integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==} + engines: {node: '>=6'} + + find-up@4.1.0: + resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + firebase-admin@10.3.0: + resolution: {integrity: sha512-A0wgMLEjyVyUE+heyMJYqHRkPVjpebhOYsa47RHdrTM4ltApcx8Tn86sUmjqxlfh09gNnILAm7a8q5+FmgBYpg==} + engines: {node: '>=12.7.0'} + + firebase@10.14.1: + resolution: {integrity: sha512-0KZxU+Ela9rUCULqFsUUOYYkjh7OM1EWdIfG6///MtXd0t2/uUIf0iNV5i0KariMhRQ5jve/OY985nrAXFaZeQ==} + + firebaseui@6.1.0: + resolution: {integrity: sha512-5WiVYVxPGMANuZKxg6KLyU1tyqIsbqf/59Zm4HrdFYwPtM5lxxB0THvgaIk4ix+hCgF0qmY89sKiktcifKzGIA==} + peerDependencies: + firebase: ^9.1.3 || ^10.0.0 + + flat-cache@3.1.1: + resolution: {integrity: sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==} + engines: {node: '>=12.0.0'} + + flat@5.0.2: + resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} + hasBin: true + + flatted@3.2.9: + resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==} + + flush-write-stream@1.1.1: + resolution: {integrity: sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==} + + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + for-each@0.3.3: + resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} + + for-in@1.0.2: + resolution: {integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==} + engines: {node: '>=0.10.0'} + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + fork-ts-checker-webpack-plugin@6.5.3: + resolution: {integrity: sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==} + engines: {node: '>=10', yarn: '>=1.0.0'} + peerDependencies: + eslint: '>= 6' + typescript: '>= 2.7' + vue-template-compiler: '*' + webpack: '>= 4' + peerDependenciesMeta: + eslint: + optional: true + vue-template-compiler: + optional: true + + form-data@4.0.5: + resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} + engines: {node: '>= 6'} + + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + + fragment-cache@0.2.1: + resolution: {integrity: sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==} + engines: {node: '>=0.10.0'} + + fresh@0.5.2: + resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} + engines: {node: '>= 0.6'} + + from2@2.3.0: + resolution: {integrity: sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==} + + fs-extra@11.2.0: + resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} + engines: {node: '>=14.14'} + + fs-extra@8.1.0: + resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} + engines: {node: '>=6 <7 || >=8'} + + fs-extra@9.1.0: + resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} + engines: {node: '>=10'} + + fs-memo@1.2.0: + resolution: {integrity: sha512-YEexkCpL4j03jn5SxaMHqcO6IuWuqm8JFUYhyCep7Ao89JIYmB8xoKhK7zXXJ9cCaNXpyNH5L3QtAmoxjoHW2w==} + + fs-minipass@2.1.0: + resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} + engines: {node: '>= 8'} + + fs-monkey@1.0.5: + resolution: {integrity: sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==} + + fs-write-stream-atomic@1.0.10: + resolution: {integrity: sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA==} + deprecated: This package is no longer supported. + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@1.2.13: + resolution: {integrity: sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==} + engines: {node: '>= 4.0'} + os: [darwin] + deprecated: Upgrade to fsevents v2 to mitigate potential security issues + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.1: + resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==} + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.6: + resolution: {integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==} + engines: {node: '>= 0.4'} + + functional-red-black-tree@1.0.1: + resolution: {integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + gaxios@4.3.3: + resolution: {integrity: sha512-gSaYYIO1Y3wUtdfHmjDUZ8LWaxJQpiavzbF5Kq53akSzvmVg0RfyOcFDbO1KJ/KCGRFz2qG+lS81F0nkr7cRJA==} + engines: {node: '>=10'} + + gcp-metadata@4.3.1: + resolution: {integrity: sha512-x850LS5N7V1F3UcV7PoupzGsyD6iVwTVvsh3tbXfkctZnBnjW5yu5z1/3k3SehF7TyoTIe78rJs02GMMy+LF+A==} + engines: {node: '>=10'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-east-asian-width@1.3.0: + resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} + engines: {node: '>=18'} + + get-intrinsic@1.2.1: + resolution: {integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-package-type@0.1.0: + resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==} + engines: {node: '>=8.0.0'} + + get-port-please@2.6.1: + resolution: {integrity: sha512-4PDSrL6+cuMM1xs6w36ZIkaKzzE0xzfVBCfebHIJ3FE8iB9oic/ECwPw3iNiD4h1AoJ5XLLBhEviFAVrZsDC5A==} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + get-stream@8.0.1: + resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} + engines: {node: '>=16'} + + get-symbol-description@1.0.0: + resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.7.2: + resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==} + + get-value@2.0.6: + resolution: {integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==} + engines: {node: '>=0.10.0'} + + giget@1.1.2: + resolution: {integrity: sha512-HsLoS07HiQ5oqvObOI+Qb2tyZH4Gj5nYGfF9qQcZNrPw+uEFhdXtgJr01aO2pWadGHucajYDLxxbtQkm97ON2A==} + hasBin: true + + giget@1.2.3: + resolution: {integrity: sha512-8EHPljDvs7qKykr6uw8b+lqLiUc/vUg+KVTI0uND4s63TdsZM2Xus3mflvF0DDG9SiM4RlCkFGL+7aAjRmV7KA==} + hasBin: true + + git-config-path@2.0.0: + resolution: {integrity: sha512-qc8h1KIQbJpp+241id3GuAtkdyJ+IK+LIVtkiFTRKRrmddDzs3SI9CvP1QYmWBFvm1I/PWRwj//of8bgAc0ltA==} + engines: {node: '>=4'} + + git-raw-commits@4.0.0: + resolution: {integrity: sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==} + engines: {node: '>=16'} + deprecated: This package is no longer maintained. For the JavaScript API, please use @conventional-changelog/git-client instead. + hasBin: true + + git-up@7.0.0: + resolution: {integrity: sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ==} + + git-url-parse@13.1.1: + resolution: {integrity: sha512-PCFJyeSSdtnbfhSNRw9Wk96dDCNx+sogTe4YNXeXSJxt7xz5hvXekuRn9JX7m+Mf4OscCu8h+mtAl3+h5Fo8lQ==} + + glob-parent@3.1.0: + resolution: {integrity: sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob-to-regexp@0.4.1: + resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + hasBin: true + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + + glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + + global-directory@4.0.1: + resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==} + engines: {node: '>=18'} + + global-modules@2.0.0: + resolution: {integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==} + engines: {node: '>=6'} + + global-prefix@3.0.0: + resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==} + engines: {node: '>=6'} + + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globals@9.18.0: + resolution: {integrity: sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==} + engines: {node: '>=0.10.0'} + + globalthis@1.0.3: + resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==} + engines: {node: '>= 0.4'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + globby@13.2.2: + resolution: {integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + globby@14.0.2: + resolution: {integrity: sha512-s3Fq41ZVh7vbbe2PN3nrW7yC7U7MFVc5c98/iTl9c2GawNMKx/J648KQRW6WKkuU8GIbbh2IXfIRQjOZnXcTnw==} + engines: {node: '>=18'} + + globjoin@0.1.4: + resolution: {integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==} + + google-auth-library@7.14.1: + resolution: {integrity: sha512-5Rk7iLNDFhFeBYc3s8l1CqzbEBcdhwR193RlD4vSNFajIcINKI8W8P0JLmBpwymHqqWbX34pJDQu39cSy/6RsA==} + engines: {node: '>=10'} + + google-gax@2.30.5: + resolution: {integrity: sha512-Jey13YrAN2hfpozHzbtrwEfEHdStJh1GwaQ2+Akh1k0Tv/EuNVSuBtHZoKSBm5wBMvNsxTsEIZ/152NrYyZgxQ==} + engines: {node: '>=10'} + hasBin: true + + google-p12-pem@3.1.4: + resolution: {integrity: sha512-HHuHmkLgwjdmVRngf5+gSmpkyaRI6QmOg77J8tkNBHhNEI62sGHyw4/+UkgyZEI7h84NbWprXDJ+sa3xOYFvTg==} + engines: {node: '>=10'} + deprecated: Package is no longer maintained + hasBin: true + + gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + gtoken@5.3.2: + resolution: {integrity: sha512-gkvEKREW7dXWF8NV8pVrKfW7WqReAmjjkMBh6lNCCGOM4ucS0r0YyXXl0r/9Yj8wcW/32ISkfc8h5mPTDbtifQ==} + engines: {node: '>=10'} + + gzip-size@6.0.0: + resolution: {integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==} + engines: {node: '>=10'} + + handlebars@4.7.8: + resolution: {integrity: sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==} + engines: {node: '>=0.4.7'} + hasBin: true + + hard-rejection@2.1.0: + resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==} + engines: {node: '>=6'} + + hard-source-webpack-plugin@0.13.1: + resolution: {integrity: sha512-r9zf5Wq7IqJHdVAQsZ4OP+dcUSvoHqDMxJlIzaE2J0TZWn3UjMMrHqwDHR8Jr/pzPfG7XxSe36E7Y8QGNdtuAw==} + engines: {node: '>=8.0.0'} + peerDependencies: + webpack: '*' + + has-ansi@2.0.0: + resolution: {integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==} + engines: {node: '>=0.10.0'} + + has-bigints@1.0.2: + resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.0: + resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} + + has-proto@1.0.1: + resolution: {integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==} + engines: {node: '>= 0.4'} + + has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + has-value@0.3.1: + resolution: {integrity: sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==} + engines: {node: '>=0.10.0'} + + has-value@1.0.0: + resolution: {integrity: sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==} + engines: {node: '>=0.10.0'} + + has-values@0.1.4: + resolution: {integrity: sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==} + engines: {node: '>=0.10.0'} + + has-values@1.0.0: + resolution: {integrity: sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==} + engines: {node: '>=0.10.0'} + + has@1.0.3: + resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==} + engines: {node: '>= 0.4.0'} + + hash-base@3.1.0: + resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==} + engines: {node: '>=4'} + + hash-stream-validation@0.2.4: + resolution: {integrity: sha512-Gjzu0Xn7IagXVkSu9cSFuK1fqzwtLwFhNhVL8IFJijRNMgUttFbBSIAzKuSIrsFMO1+g1RlsoN49zPIbwPDMGQ==} + + hash-sum@1.0.2: + resolution: {integrity: sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==} + + hash-sum@2.0.0: + resolution: {integrity: sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==} + + hash.js@1.1.7: + resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + highlight.js@11.11.1: + resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} + engines: {node: '>=12.0.0'} + + hmac-drbg@1.0.1: + resolution: {integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==} + + hookable@4.4.1: + resolution: {integrity: sha512-KWjZM8C7IVT2qne5HTXjM6R6VnRfjfRlf/oCnHd+yFxoHO1DzOl6B9LzV/VqGQK/IrFewq+EG+ePVrE9Tpc3fg==} + + hookable@5.5.3: + resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} + + hosted-git-info@2.8.9: + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + + hosted-git-info@4.1.0: + resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} + engines: {node: '>=10'} + + html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + + html-entities@2.4.0: + resolution: {integrity: sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + html-minifier-terser@5.1.1: + resolution: {integrity: sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==} + engines: {node: '>=6'} + hasBin: true + + html-minifier-terser@7.2.0: + resolution: {integrity: sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==} + engines: {node: ^14.13.1 || >=16.0.0} + hasBin: true + + html-tags@2.0.0: + resolution: {integrity: sha512-+Il6N8cCo2wB/Vd3gqy/8TZhTD3QvcVeQLCnZiGkGCH3JP28IgGAY41giccp2W4R3jfyJPAP318FQTa1yU7K7g==} + engines: {node: '>=4'} + + html-tags@3.3.1: + resolution: {integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==} + engines: {node: '>=8'} + + html-webpack-plugin@4.5.2: + resolution: {integrity: sha512-q5oYdzjKUIPQVjOosjgvCHQOv9Ett9CYYHlgvJeXG0qQvdSojnBq4vAdQBwn1+yGveAwHCoe/rMR86ozX3+c2A==} + engines: {node: '>=6.9'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + + htmlparser2@6.1.0: + resolution: {integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==} + + htmlparser2@8.0.2: + resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==} + + http-errors@2.0.0: + resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} + engines: {node: '>= 0.8'} + + http-parser-js@0.5.8: + resolution: {integrity: sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==} + + http-proxy-agent@5.0.0: + resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} + engines: {node: '>= 6'} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-browserify@1.0.0: + resolution: {integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==} + + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + human-signals@5.0.0: + resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} + engines: {node: '>=16.17.0'} + + hyperdyperid@1.2.0: + resolution: {integrity: sha512-Y93lCzHYgGWdrJ66yIktxiaGULYc6oGiABxhcO5AufBeOyoIdZF7bIfLaOrbM0iGIOXQQgxxRrFEnb+Y6w1n4A==} + engines: {node: '>=10.18'} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + icss-utils@5.1.0: + resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + idb@7.1.1: + resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + iferr@0.1.5: + resolution: {integrity: sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==} + + ignore@5.2.4: + resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} + engines: {node: '>= 4'} + + ignore@5.3.1: + resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} + engines: {node: '>= 4'} + + import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + import-lazy@4.0.0: + resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==} + engines: {node: '>=8'} + + import-local@3.2.0: + resolution: {integrity: sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==} + engines: {node: '>=8'} + hasBin: true + + import-meta-resolve@4.2.0: + resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + indent-string@4.0.0: + resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} + engines: {node: '>=8'} + + indent-string@5.0.0: + resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} + engines: {node: '>=12'} + + infer-owner@1.0.4: + resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.3: + resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + ini@4.1.1: + resolution: {integrity: sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + inquirer@7.3.3: + resolution: {integrity: sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==} + engines: {node: '>=8.0.0'} + + internal-slot@1.0.5: + resolution: {integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==} + engines: {node: '>= 0.4'} + + invariant@2.2.4: + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + + ip@2.0.1: + resolution: {integrity: sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==} + + is-accessor-descriptor@0.1.6: + resolution: {integrity: sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==} + engines: {node: '>=0.10.0'} + deprecated: Please upgrade to v0.1.7 + + is-accessor-descriptor@1.0.0: + resolution: {integrity: sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==} + engines: {node: '>=0.10.0'} + deprecated: Please upgrade to v1.0.1 + + is-array-buffer@3.0.2: + resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-bigint@1.0.4: + resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==} + + is-binary-path@1.0.1: + resolution: {integrity: sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==} + engines: {node: '>=0.10.0'} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-boolean-object@1.1.2: + resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==} + engines: {node: '>= 0.4'} + + is-buffer@1.1.6: + resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} + + is-builtin-module@3.2.1: + resolution: {integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==} + engines: {node: '>=6'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.13.0: + resolution: {integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==} + + is-data-descriptor@0.1.4: + resolution: {integrity: sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==} + engines: {node: '>=0.10.0'} + deprecated: Please upgrade to v0.1.5 + + is-data-descriptor@1.0.0: + resolution: {integrity: sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==} + engines: {node: '>=0.10.0'} + deprecated: Please upgrade to v1.0.1 + + is-date-object@1.0.5: + resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==} + engines: {node: '>= 0.4'} + + is-descriptor@0.1.6: + resolution: {integrity: sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==} + engines: {node: '>=0.10.0'} + + is-descriptor@1.0.2: + resolution: {integrity: sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==} + engines: {node: '>=0.10.0'} + + is-extendable@0.1.1: + resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==} + engines: {node: '>=0.10.0'} + + is-extendable@1.0.1: + resolution: {integrity: sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==} + engines: {node: '>=0.10.0'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-fullwidth-code-point@4.0.0: + resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==} + engines: {node: '>=12'} + + is-fullwidth-code-point@5.0.0: + resolution: {integrity: sha512-OVa3u9kkBbw7b8Xw5F9P+D/T9X+Z4+JruYVNapTjPYZYUznQ5YfWeFkOj606XYYW8yugTfC8Pj0hYqvi4ryAhA==} + engines: {node: '>=18'} + + is-generator-fn@2.1.0: + resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==} + engines: {node: '>=6'} + + is-glob@3.1.0: + resolution: {integrity: sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==} + engines: {node: '>=0.10.0'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-https@2.0.2: + resolution: {integrity: sha512-UfUCKVQH/6PQRCh5Qk9vNu4feLZiFmV/gr8DjbtJD0IrCRIDTA6E+d/AVFGPulI5tqK5W45fYbn1Nir1O99rFw==} + + is-negative-zero@2.0.2: + resolution: {integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==} + engines: {node: '>= 0.4'} + + is-number-object@1.0.7: + resolution: {integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==} + engines: {node: '>= 0.4'} + + is-number@3.0.0: + resolution: {integrity: sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==} + engines: {node: '>=0.10.0'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-obj@2.0.0: + resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} + engines: {node: '>=8'} + + is-path-inside@3.0.3: + resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==} + engines: {node: '>=8'} + + is-plain-obj@1.1.0: + resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} + engines: {node: '>=0.10.0'} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-plain-object@2.0.4: + resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} + engines: {node: '>=0.10.0'} + + is-plain-object@5.0.0: + resolution: {integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==} + engines: {node: '>=0.10.0'} + + is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + + is-regex@1.1.4: + resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.2: + resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==} + + is-ssh@1.4.0: + resolution: {integrity: sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==} + + is-stream-ended@0.1.4: + resolution: {integrity: sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw==} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-string@1.0.7: + resolution: {integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==} + engines: {node: '>= 0.4'} + + is-symbol@1.0.4: + resolution: {integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.12: + resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==} + engines: {node: '>= 0.4'} + + is-typedarray@1.0.0: + resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==} + + is-weakref@1.0.2: + resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==} + + is-whitespace@0.3.0: + resolution: {integrity: sha512-RydPhl4S6JwAyj0JJjshWJEFG6hNye3pZFBRZaTUfZFwGHxzppNaNOVgQuS/E/SlhrApuMXrpnK1EEIXfdo3Dg==} + engines: {node: '>=0.10.0'} + + is-windows@1.0.2: + resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==} + engines: {node: '>=0.10.0'} + + is-wsl@1.1.0: + resolution: {integrity: sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==} + engines: {node: '>=4'} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + isobject@2.1.0: + resolution: {integrity: sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==} + engines: {node: '>=0.10.0'} + + isobject@3.0.1: + resolution: {integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==} + engines: {node: '>=0.10.0'} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-instrument@6.0.3: + resolution: {integrity: sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==} + engines: {node: '>=10'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-lib-source-maps@5.0.6: + resolution: {integrity: sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==} + engines: {node: '>=10'} + + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} + engines: {node: '>=8'} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jake@10.9.4: + resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==} + engines: {node: '>=10'} + hasBin: true + + jest-changed-files@30.2.0: + resolution: {integrity: sha512-L8lR1ChrRnSdfeOvTrwZMlnWV8G/LLjQ0nG9MBclwWZidA2N5FviRki0Bvh20WRMOX31/JYvzdqTJrk5oBdydQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-circus@30.2.0: + resolution: {integrity: sha512-Fh0096NC3ZkFx05EP2OXCxJAREVxj1BcW/i6EWqqymcgYKWjyyDpral3fMxVcHXg6oZM7iULer9wGRFvfpl+Tg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-cli@30.2.0: + resolution: {integrity: sha512-Os9ukIvADX/A9sLt6Zse3+nmHtHaE6hqOsjQtNiugFTbKRHYIYtZXNGNK9NChseXy7djFPjndX1tL0sCTlfpAA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + jest-config@30.2.0: + resolution: {integrity: sha512-g4WkyzFQVWHtu6uqGmQR4CQxz/CH3yDSlhzXMWzNjDx843gYjReZnMRanjRCq5XZFuQrGDxgUaiYWE8BRfVckA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + '@types/node': '*' + esbuild-register: '>=3.4.0' + ts-node: '>=9.0.0' + peerDependenciesMeta: + '@types/node': + optional: true + esbuild-register: + optional: true + ts-node: + optional: true + + jest-diff@30.2.0: + resolution: {integrity: sha512-dQHFo3Pt4/NLlG5z4PxZ/3yZTZ1C7s9hveiOj+GCN+uT109NC2QgsoVZsVOAvbJ3RgKkvyLGXZV9+piDpWbm6A==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-docblock@30.2.0: + resolution: {integrity: sha512-tR/FFgZKS1CXluOQzZvNH3+0z9jXr3ldGSD8bhyuxvlVUwbeLOGynkunvlTMxchC5urrKndYiwCFC0DLVjpOCA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-each@30.2.0: + resolution: {integrity: sha512-lpWlJlM7bCUf1mfmuqTA8+j2lNURW9eNafOy99knBM01i5CQeY5UH1vZjgT9071nDJac1M4XsbyI44oNOdhlDQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-environment-jsdom@30.2.0: + resolution: {integrity: sha512-zbBTiqr2Vl78pKp/laGBREYzbZx9ZtqPjOK4++lL4BNDhxRnahg51HtoDrk9/VjIy9IthNEWdKVd7H5bqBhiWQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + + jest-environment-node@30.2.0: + resolution: {integrity: sha512-ElU8v92QJ9UrYsKrxDIKCxu6PfNj4Hdcktcn0JX12zqNdqWHB0N+hwOnnBBXvjLd2vApZtuLUGs1QSY+MsXoNA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-haste-map@30.2.0: + resolution: {integrity: sha512-sQA/jCb9kNt+neM0anSj6eZhLZUIhQgwDt7cPGjumgLM4rXsfb9kpnlacmvZz3Q5tb80nS+oG/if+NBKrHC+Xw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-leak-detector@30.2.0: + resolution: {integrity: sha512-M6jKAjyzjHG0SrQgwhgZGy9hFazcudwCNovY/9HPIicmNSBuockPSedAP9vlPK6ONFJ1zfyH/M2/YYJxOz5cdQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-matcher-utils@30.2.0: + resolution: {integrity: sha512-dQ94Nq4dbzmUWkQ0ANAWS9tBRfqCrn0bV9AMYdOi/MHW726xn7eQmMeRTpX2ViC00bpNaWXq+7o4lIQ3AX13Hg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-message-util@30.2.0: + resolution: {integrity: sha512-y4DKFLZ2y6DxTWD4cDe07RglV88ZiNEdlRfGtqahfbIjfsw1nMCPx49Uev4IA/hWn3sDKyAnSPwoYSsAEdcimw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-mock@30.2.0: + resolution: {integrity: sha512-JNNNl2rj4b5ICpmAcq+WbLH83XswjPbjH4T7yvGzfAGCPh1rw+xVNbtk+FnRslvt9lkCcdn9i1oAoKUuFsOxRw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-pnp-resolver@1.2.3: + resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==} + engines: {node: '>=6'} + peerDependencies: + jest-resolve: '*' + peerDependenciesMeta: + jest-resolve: + optional: true + + jest-regex-util@30.0.1: + resolution: {integrity: sha512-jHEQgBXAgc+Gh4g0p3bCevgRCVRkB4VB70zhoAE48gxeSr1hfUOsM/C2WoJgVL7Eyg//hudYENbm3Ne+/dRVVA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-resolve-dependencies@30.2.0: + resolution: {integrity: sha512-xTOIGug/0RmIe3mmCqCT95yO0vj6JURrn1TKWlNbhiAefJRWINNPgwVkrVgt/YaerPzY3iItufd80v3lOrFJ2w==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-resolve@30.2.0: + resolution: {integrity: sha512-TCrHSxPlx3tBY3hWNtRQKbtgLhsXa1WmbJEqBlTBrGafd5fiQFByy2GNCEoGR+Tns8d15GaL9cxEzKOO3GEb2A==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-runner@30.2.0: + resolution: {integrity: sha512-PqvZ2B2XEyPEbclp+gV6KO/F1FIFSbIwewRgmROCMBo/aZ6J1w8Qypoj2pEOcg3G2HzLlaP6VUtvwCI8dM3oqQ==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-runtime@30.2.0: + resolution: {integrity: sha512-p1+GVX/PJqTucvsmERPMgCPvQJpFt4hFbM+VN3n8TMo47decMUcJbt+rgzwrEme0MQUA/R+1de2axftTHkKckg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-snapshot@30.2.0: + resolution: {integrity: sha512-5WEtTy2jXPFypadKNpbNkZ72puZCa6UjSr/7djeecHWOu7iYhSXSnHScT8wBz3Rn8Ena5d5RYRcsyKIeqG1IyA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-util@29.7.0: + resolution: {integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-util@30.2.0: + resolution: {integrity: sha512-QKNsM0o3Xe6ISQU869e+DhG+4CK/48aHYdJZGlFQVTjnbvgpcKyxpzk29fGiO7i/J8VENZ+d2iGnSsvmuHywlA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-validate@30.2.0: + resolution: {integrity: sha512-FBGWi7dP2hpdi8nBoWxSsLvBFewKAg0+uSQwBaof4Y4DPgBabXgpSYC5/lR7VmnIlSpASmCi/ntRWPbv7089Pw==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-watcher@30.2.0: + resolution: {integrity: sha512-PYxa28dxJ9g777pGm/7PrbnMeA0Jr7osHP9bS7eJy9DuAjMgdGtxgf0uKMyoIsTWAkIbUW5hSDdJ3urmgXBqxg==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest-worker@26.6.2: + resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==} + engines: {node: '>= 10.13.0'} + + jest-worker@27.5.1: + resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} + engines: {node: '>= 10.13.0'} + + jest-worker@29.7.0: + resolution: {integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==} + engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} + + jest-worker@30.2.0: + resolution: {integrity: sha512-0Q4Uk8WF7BUwqXHuAjc23vmopWJw5WH7w2tqBoUOZpOjW/ZnR44GXXd1r82RvnmI2GZge3ivrYXk/BE2+VtW2g==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + jest@30.2.0: + resolution: {integrity: sha512-F26gjC0yWN8uAA5m5Ss8ZQf5nDHWGlN/xWZIh8S5SRbsEKBovwZhxGd6LJlbZYxBgCYOtreSUyb8hpXyGC5O4A==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 + peerDependenciesMeta: + node-notifier: + optional: true + + jiti@1.20.0: + resolution: {integrity: sha512-3TV69ZbrvV6U5DfQimop50jE9Dl6J8O1ja1dvBbMba/sZ3YBEQqJ2VZRoQPVnhlzjNtU1vaXRZVrVjU4qtm8yA==} + hasBin: true + + jiti@1.21.0: + resolution: {integrity: sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==} + hasBin: true + + jiti@1.21.6: + resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} + hasBin: true + + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + + jose@2.0.7: + resolution: {integrity: sha512-5hFWIigKqC+e/lRyQhfnirrAqUdIPMB7SJRqflJaO29dW7q5DFvH1XCSTmv6PQ6pb++0k6MJlLRoS0Wv4s38Wg==} + engines: {node: '>=10.13.0 < 13 || >=13.7.0'} + + js-beautify@1.14.9: + resolution: {integrity: sha512-coM7xq1syLcMyuVGyToxcj2AlzhkDjmfklL8r0JgJ7A76wyGMpJ1oA35mr4APdYNO/o/4YY8H54NQIJzhMbhBg==} + engines: {node: '>=12'} + hasBin: true + + js-tokens@3.0.2: + resolution: {integrity: sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + + js-yaml@3.14.1: + resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + hasBin: true + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + jsdom@26.1.0: + resolution: {integrity: sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==} + engines: {node: '>=18'} + peerDependencies: + canvas: ^3.0.0 + peerDependenciesMeta: + canvas: + optional: true + + jsesc@0.5.0: + resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==} + hasBin: true + + jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-bigint@1.0.0: + resolution: {integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==} + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-better-errors@1.0.2: + resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-schema-traverse@1.0.0: + resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@0.5.1: + resolution: {integrity: sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==} + hasBin: true + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonfile@4.0.0: + resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} + + jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + + jsonwebtoken@8.5.1: + resolution: {integrity: sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==} + engines: {node: '>=4', npm: '>=1.4.28'} + + jwa@1.4.1: + resolution: {integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==} + + jwa@2.0.0: + resolution: {integrity: sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==} + + jwks-rsa@2.1.5: + resolution: {integrity: sha512-IODtn1SwEm7n6GQZnQLY0oxKDrMh7n/jRH1MzE8mlxWMrh2NnMyOsXTebu8vJ1qCpmuTJcL4DdiE0E4h8jnwsA==} + engines: {node: '>=10 < 13 || >=14'} + + jws@3.2.2: + resolution: {integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==} + + jws@4.0.0: + resolution: {integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==} + + kasi@2.0.1: + resolution: {integrity: sha512-8qhiHZ1BN26ig1+jQ9fWEk6dj8T1wuxs00QRJfXIANI4scto1EuPUgqj+mxHls52WBfdTNJGQ8yYw9rDpWUcgQ==} + + keyv@4.5.3: + resolution: {integrity: sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==} + + kind-of@3.2.2: + resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} + engines: {node: '>=0.10.0'} + + kind-of@4.0.0: + resolution: {integrity: sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==} + engines: {node: '>=0.10.0'} + + kind-of@5.1.0: + resolution: {integrity: sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==} + engines: {node: '>=0.10.0'} + + kind-of@6.0.3: + resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==} + engines: {node: '>=0.10.0'} + + klona@2.0.6: + resolution: {integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==} + engines: {node: '>= 8'} + + knitwork@1.0.0: + resolution: {integrity: sha512-dWl0Dbjm6Xm+kDxhPQJsCBTxrJzuGl0aP9rhr+TG8D3l+GL90N8O8lYUi7dTSAN2uuDqCtNgb6aEuQH5wsiV8Q==} + + knitwork@1.1.0: + resolution: {integrity: sha512-oHnmiBUVHz1V+URE77PNot2lv3QiYU2zQf1JjOVkMt3YDKGbu8NAFr+c4mcNOhdsGrB/VpVbRwPwhiXrPhxQbw==} + + known-css-properties@0.29.0: + resolution: {integrity: sha512-Ne7wqW7/9Cz54PDt4I3tcV+hAyat8ypyOGzYRJQfdxnnjeWsTxt1cy8pjvvKeI5kfXuyvULyeeAvwvvtAX3ayQ==} + + last-call-webpack-plugin@3.0.0: + resolution: {integrity: sha512-7KI2l2GIZa9p2spzPIVZBYyNKkN+e/SQPpnjlTiPhdbDW3F86tdKKELxKpzJ5sgU19wQWsACULZmpTPYHeWO5w==} + + launch-editor-middleware@2.8.0: + resolution: {integrity: sha512-0Az27jnPR2RgkUoZoLHluM5gg9zHeg7hPsUZESJxcTV8Rs6Fed+Nof7Lb2HmpsE8lN/3YzpU+mvK5exYWSftWw==} + + launch-editor@2.8.0: + resolution: {integrity: sha512-vJranOAJrI/llyWGRQqiDM+adrw+k83fvmmx3+nV47g3+36xM15jE+zyZ6Ffel02+xSvuM0b2GDRosXZkbb6wA==} + + leven@3.1.0: + resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==} + engines: {node: '>=6'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + libphonenumber-js@1.12.36: + resolution: {integrity: sha512-woWhKMAVx1fzzUnMCyOzglgSgf6/AFHLASdOBcchYCyvWSGWt12imw3iu2hdI5d4dGZRsNWAmWiz37sDKUPaRQ==} + + lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + limiter@1.1.5: + resolution: {integrity: sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + lint-staged@16.1.4: + resolution: {integrity: sha512-xy7rnzQrhTVGKMpv6+bmIA3C0yET31x8OhKBYfvGo0/byeZ6E0BjGARrir3Kg/RhhYHutpsi01+2J5IpfVoueA==} + engines: {node: '>=20.17'} + hasBin: true + + listr2@9.0.1: + resolution: {integrity: sha512-SL0JY3DaxylDuo/MecFeiC+7pedM0zia33zl0vcjgwcq1q1FWWF1To9EIauPbl8GbMCU0R2e0uJ8bZunhYKD2g==} + engines: {node: '>=20.0.0'} + + loader-runner@2.4.0: + resolution: {integrity: sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==} + engines: {node: '>=4.3.0 <5.0.0 || >=5.10'} + + loader-runner@4.3.1: + resolution: {integrity: sha512-IWqP2SCPhyVFTBtRcgMHdzlf9ul25NwaFx4wCEH/KjAXuuHY4yNjvPXsBokp8jCB936PyWRaPKUNh8NvylLp2Q==} + engines: {node: '>=6.11.5'} + + loader-utils@1.4.2: + resolution: {integrity: sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==} + engines: {node: '>=4.0.0'} + + loader-utils@2.0.4: + resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==} + engines: {node: '>=8.9.0'} + + local-pkg@0.4.3: + resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} + engines: {node: '>=14'} + + local-pkg@0.5.0: + resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} + engines: {node: '>=14'} + + locate-path@3.0.0: + resolution: {integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==} + engines: {node: '>=6'} + + locate-path@5.0.0: + resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==} + engines: {node: '>=8'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash._reinterpolate@3.0.0: + resolution: {integrity: sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==} + + lodash.camelcase@4.3.0: + resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==} + + lodash.clonedeep@4.5.0: + resolution: {integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==} + + lodash.debounce@4.0.8: + resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + + lodash.includes@4.3.0: + resolution: {integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==} + + lodash.isboolean@3.0.3: + resolution: {integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==} + + lodash.isinteger@4.0.4: + resolution: {integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==} + + lodash.isnumber@3.0.3: + resolution: {integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==} + + lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + + lodash.isstring@4.0.1: + resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} + + lodash.kebabcase@4.1.1: + resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==} + + lodash.memoize@4.1.2: + resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + lodash.mergewith@4.6.2: + resolution: {integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==} + + lodash.once@4.1.1: + resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} + + lodash.template@4.5.0: + resolution: {integrity: sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==} + deprecated: This package is deprecated. Use https://socket.dev/npm/package/eta instead. + + lodash.templatesettings@4.2.0: + resolution: {integrity: sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==} + + lodash.truncate@4.4.2: + resolution: {integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==} + + lodash.unionby@4.8.0: + resolution: {integrity: sha512-e60kn4GJIunNkw6v9MxRnUuLYI/Tyuanch7ozoCtk/1irJTYBj+qNTxr5B3qVflmJhwStJBv387Cb+9VOfABMg==} + + lodash.uniq@4.5.0: + resolution: {integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + log-update@6.1.0: + resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} + engines: {node: '>=18'} + + long@4.0.0: + resolution: {integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==} + + long@5.2.3: + resolution: {integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lower-case@2.0.2: + resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@4.0.2: + resolution: {integrity: sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==} + + lru-cache@4.1.5: + resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + + lru-memoizer@2.2.0: + resolution: {integrity: sha512-QfOZ6jNkxCcM/BkIPnFsqDhtrazLRsghi9mBwFAzol5GCvj4EkFT899Za3+QwikCg5sRX8JstioBDwOxEyzaNw==} + + magic-string@0.30.10: + resolution: {integrity: sha512-iIRwTIf0QKV3UAnYK4PU8uiEc4SRh5jX0mwpIwETPpHdhVM4f53RSwS/vXvN1JhGX+Cs7B8qIq3d6AH49O5fAQ==} + + magic-string@0.30.4: + resolution: {integrity: sha512-Q/TKtsC5BPm0kGqgBIF9oXAs/xEf2vRKiIB4wCRQTJOQIByZ1d+NnUOotvJOvNpi5RNIgVOMC3pOuaP1ZTDlVg==} + engines: {node: '>=12'} + + make-dir@1.3.0: + resolution: {integrity: sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==} + engines: {node: '>=4'} + + make-dir@2.1.0: + resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} + engines: {node: '>=6'} + + make-dir@3.1.0: + resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} + engines: {node: '>=8'} + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + make-error@1.3.6: + resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} + + makeerror@1.0.12: + resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==} + + map-cache@0.2.2: + resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==} + engines: {node: '>=0.10.0'} + + map-obj@1.0.1: + resolution: {integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==} + engines: {node: '>=0.10.0'} + + map-obj@4.3.0: + resolution: {integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==} + engines: {node: '>=8'} + + map-visit@1.0.0: + resolution: {integrity: sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==} + engines: {node: '>=0.10.0'} + + markdown-table@2.0.0: + resolution: {integrity: sha512-Ezda85ToJUBhM6WGaG6veasyym+Tbs3cMAw/ZhOPqXiYsr0jgocBV3j3nx+4lk47plLlIqjwuTm/ywVI+zjJ/A==} + + material-design-lite@1.3.0: + resolution: {integrity: sha512-ao76b0bqSTKcEMt7Pui+J/S3eVF0b3GWfuKUwfe2lP5DKlLZOwBq37e0/bXEzxrw7/SuHAuYAdoCwY6mAYhrsg==} + engines: {node: '>=0.12.0'} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mathml-tag-names@2.1.3: + resolution: {integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==} + + md5.js@1.3.5: + resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} + + mdn-data@2.0.14: + resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} + + mdn-data@2.0.28: + resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==} + + mdn-data@2.0.30: + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + + memfs@3.5.3: + resolution: {integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==} + engines: {node: '>= 4.0.0'} + + memfs@4.9.3: + resolution: {integrity: sha512-bsYSSnirtYTWi1+OPMFb0M048evMKyUYe0EbtuGQgq6BVQM1g1W8/KIUJCCvjgI/El0j6Q4WsmMiBwLUBSw8LA==} + engines: {node: '>= 4.0.0'} + + memory-fs@0.4.1: + resolution: {integrity: sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==} + + memory-fs@0.5.0: + resolution: {integrity: sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==} + engines: {node: '>=4.3.0 <5.0.0 || >=5.10'} + + meow@10.1.5: + resolution: {integrity: sha512-/d+PQ4GKmGvM9Bee/DPa8z3mXs/pkvJE2KEThngVNOqtmljC6K7NMPxtc2JeZYTmpWb9k/TmxjeL18ez3h7vCw==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + meow@12.1.1: + resolution: {integrity: sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==} + engines: {node: '>=16.10'} + + meow@13.2.0: + resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} + engines: {node: '>=18'} + + merge-source-map@1.1.0: + resolution: {integrity: sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@3.1.10: + resolution: {integrity: sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==} + engines: {node: '>=0.10.0'} + + micromatch@4.0.5: + resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==} + engines: {node: '>=8.6'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + miller-rabin@4.0.1: + resolution: {integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==} + hasBin: true + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-db@1.54.0: + resolution: {integrity: sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + mime@2.5.2: + resolution: {integrity: sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==} + engines: {node: '>=4.0.0'} + hasBin: true + + mime@3.0.0: + resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==} + engines: {node: '>=10.0.0'} + hasBin: true + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-fn@4.0.0: + resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} + engines: {node: '>=12'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + min-indent@1.0.1: + resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} + engines: {node: '>=4'} + + minimalistic-assert@1.0.1: + resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==} + + minimalistic-crypto-utils@1.0.1: + resolution: {integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==} + + minimatch@3.0.8: + resolution: {integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + + minimatch@5.1.9: + resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} + engines: {node: '>=10'} + + minimatch@9.0.1: + resolution: {integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==} + engines: {node: '>=16 || 14 >=14.17'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist-options@4.1.0: + resolution: {integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==} + engines: {node: '>= 6'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass-collect@1.0.2: + resolution: {integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==} + engines: {node: '>= 8'} + + minipass-flush@1.0.5: + resolution: {integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==} + engines: {node: '>= 8'} + + minipass-pipeline@1.2.4: + resolution: {integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==} + engines: {node: '>=8'} + + minipass@3.3.6: + resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} + engines: {node: '>=8'} + + minipass@5.0.0: + resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} + engines: {node: '>=8'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + minizlib@2.1.2: + resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} + engines: {node: '>= 8'} + + mississippi@3.0.0: + resolution: {integrity: sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==} + engines: {node: '>=4.0.0'} + + mixin-deep@1.3.2: + resolution: {integrity: sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==} + engines: {node: '>=0.10.0'} + + mkdirp@0.5.6: + resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} + hasBin: true + + mkdirp@1.0.4: + resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} + engines: {node: '>=10'} + hasBin: true + + mlly@1.4.2: + resolution: {integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==} + + mlly@1.7.1: + resolution: {integrity: sha512-rrVRZRELyQzrIUAVMHxP97kv+G786pHmOKzuFII8zDYahFBS7qnHh2AlYSl1GAHhaMPCz6/oHjVMcfFYgFYHgA==} + + mlly@1.7.3: + resolution: {integrity: sha512-xUsx5n/mN0uQf4V548PKQ+YShA4/IW0KI1dZhrNrPCLG+xizETbHTkOa1f8/xut9JRPp8kQuMnz0oqwkTiLo/A==} + + moment@2.30.1: + resolution: {integrity: sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==} + + move-concurrently@1.0.1: + resolution: {integrity: sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ==} + deprecated: This package is no longer supported. + + mri@1.2.0: + resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==} + engines: {node: '>=4'} + + mrmime@1.0.1: + resolution: {integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==} + engines: {node: '>=10'} + + ms@2.0.0: + resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + mustache@2.3.2: + resolution: {integrity: sha512-KpMNwdQsYz3O/SBS1qJ/o3sqUJ5wSb8gb0pul8CO0S56b9Y2ALm8zCfsjPXsqGFfoNBkDwZuZIAjhsZI03gYVQ==} + engines: {npm: '>=1.4.0'} + hasBin: true + + mute-stream@0.0.8: + resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==} + + nan@2.18.0: + resolution: {integrity: sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==} + + nano-spawn@1.0.2: + resolution: {integrity: sha512-21t+ozMQDAL/UGgQVBbZ/xXvNO10++ZPuTmKRO8k9V3AClVRht49ahtDjfY8l1q6nSHOrE5ASfthzH3ol6R/hg==} + engines: {node: '>=20.17'} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nanoid@3.3.8: + resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nanomatch@1.2.13: + resolution: {integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==} + engines: {node: '>=0.10.0'} + + napi-postinstall@0.3.3: + resolution: {integrity: sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + negotiator@0.6.3: + resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} + engines: {node: '>= 0.6'} + + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + no-case@3.0.4: + resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} + + node-addon-api@1.7.2: + resolution: {integrity: sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==} + + node-cache@4.2.1: + resolution: {integrity: sha512-BOb67bWg2dTyax5kdef5WfU3X8xu4wPg+zHzkvls0Q/QpYycIFRLEEIdAx9Wma43DxG6Qzn4illdZoYseKWa4A==} + engines: {node: '>= 0.4.6'} + + node-fetch-native@1.6.7: + resolution: {integrity: sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==} + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + node-forge@1.3.1: + resolution: {integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==} + engines: {node: '>= 6.13.0'} + + node-html-parser@6.1.13: + resolution: {integrity: sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg==} + + node-int64@0.4.0: + resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} + + node-libs-browser@2.2.1: + resolution: {integrity: sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==} + + node-object-hash@1.4.2: + resolution: {integrity: sha512-UdS4swXs85fCGWWf6t6DMGgpN/vnlKeSGEQ7hJcrs7PBFoxoKLmibc3QRb7fwiYsjdL7PX8iI/TMSlZ90dgHhQ==} + engines: {node: '>=0.10.0'} + + node-releases@2.0.27: + resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + + node-res@5.0.1: + resolution: {integrity: sha512-YOleO9c7MAqoHC+Ccu2vzvV1fL6Ku49gShq3PIMKWHRgrMSih3XcwL05NbLBi6oU2J471gTBfdpVVxwT6Pfhxg==} + + nopt@6.0.0: + resolution: {integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==} + engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + hasBin: true + + normalize-package-data@2.5.0: + resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + + normalize-package-data@3.0.3: + resolution: {integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==} + engines: {node: '>=10'} + + normalize-path@2.1.1: + resolution: {integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==} + engines: {node: '>=0.10.0'} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + + normalize-url@1.9.1: + resolution: {integrity: sha512-A48My/mtCklowHBlI8Fq2jFWK4tX4lJ5E6ytFsSOq1fzpvT0SQSgKhSg7lN5c2uYFOrUAOQp6zhhJnpp1eMloQ==} + engines: {node: '>=4'} + + normalize-url@6.1.0: + resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} + engines: {node: '>=10'} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + npm-run-path@5.3.0: + resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + nuxt-highlightjs@1.0.3: + resolution: {integrity: sha512-3UEEyVYwjN+tg+gFF2fC/K4+xMiGCQlZ+3c19f3MCa5l90JtV7QXfU/2NpTq3yY3BeAgAYSwLVbP1SWOhVsXaw==} + + nuxt@2.18.1: + resolution: {integrity: sha512-SZFOLDKgCfLu23BrQE0YYNWeoi/h+fw07TNDNDzRfbmMvQlStgTBG7lqeELytXdQnaPKWjWAYo12K7pPPRZb9Q==} + deprecated: Nuxt 2 has reached EOL and is no longer actively maintained. See https://nuxt.com/blog/nuxt2-eol for more details. + hasBin: true + + nwsapi@2.2.22: + resolution: {integrity: sha512-ujSMe1OWVn55euT1ihwCI1ZcAaAU3nxUiDwfDQldc51ZXaB9m2AyOn6/jh1BLe2t/G8xd6uKG1UBF2aZJeg2SQ==} + + nypm@0.3.9: + resolution: {integrity: sha512-BI2SdqqTHg2d4wJh8P9A1W+bslg33vOE9IZDY6eR2QC+Pu1iNBVZUqczrd43rJb+fMzHU7ltAYKsEFY/kHMFcw==} + engines: {node: ^14.16.0 || >=16.10.0} + hasBin: true + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-copy@0.1.0: + resolution: {integrity: sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==} + engines: {node: '>=0.10.0'} + + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + object-inspect@1.12.3: + resolution: {integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object-visit@1.0.1: + resolution: {integrity: sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==} + engines: {node: '>=0.10.0'} + + object.assign@4.1.4: + resolution: {integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.7: + resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==} + engines: {node: '>= 0.4'} + + object.getownpropertydescriptors@2.1.7: + resolution: {integrity: sha512-PrJz0C2xJ58FNn11XV2lr4Jt5Gzl94qpy9Lu0JlfEj14z88sqbSBJCBEzdlNUCzY2gburhbrwOZ5BHCmuNUy0g==} + engines: {node: '>= 0.8'} + + object.groupby@1.0.1: + resolution: {integrity: sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==} + + object.pick@1.3.0: + resolution: {integrity: sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==} + engines: {node: '>=0.10.0'} + + object.values@1.1.7: + resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==} + engines: {node: '>= 0.4'} + + ohash@1.1.3: + resolution: {integrity: sha512-zuHHiGTYTA1sYJ/wZN+t5HKZaH23i4yI1HMwbuXm24Nid7Dv0KcuRlKoNKS9UNfAVSBlnGLcuQrnOKWOZoEGaw==} + + ohash@1.1.4: + resolution: {integrity: sha512-FlDryZAahJmEF3VR3w1KogSEdWX3WhA5GPakFx4J81kEAiHyLMpdLLElS8n8dfNadMgAne/MywcvmogzscVt4g==} + + on-finished@2.3.0: + resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} + engines: {node: '>= 0.8'} + + on-finished@2.4.1: + resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} + engines: {node: '>= 0.8'} + + on-headers@1.0.2: + resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==} + engines: {node: '>= 0.8'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + onetime@6.0.0: + resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} + engines: {node: '>=12'} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + opener@1.5.2: + resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==} + hasBin: true + + optimize-css-assets-webpack-plugin@6.0.1: + resolution: {integrity: sha512-BshV2UZPfggZLdUfN3zFBbG4sl/DynUI+YCB6fRRDWaqO2OiWN8GPcp4Y0/fEV6B3k9Hzyk3czve3V/8B/SzKQ==} + peerDependencies: + webpack: ^4.0.0 + + optionator@0.9.3: + resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==} + engines: {node: '>= 0.8.0'} + + os-browserify@0.3.0: + resolution: {integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==} + + os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + + p-limit@2.3.0: + resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} + engines: {node: '>=6'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@3.0.0: + resolution: {integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==} + engines: {node: '>=6'} + + p-locate@4.1.0: + resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==} + engines: {node: '>=8'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + p-map@4.0.0: + resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==} + engines: {node: '>=10'} + + p-try@2.2.0: + resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} + engines: {node: '>=6'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + + parallel-transform@1.2.0: + resolution: {integrity: sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==} + + param-case@3.0.4: + resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-asn1@5.1.6: + resolution: {integrity: sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==} + + parse-git-config@3.0.0: + resolution: {integrity: sha512-wXoQGL1D+2COYWCD35/xbiKma1Z15xvZL8cI25wvxzled58V51SJM04Urt/uznS900iQor7QO04SgdfT/XlbuA==} + engines: {node: '>=8'} + + parse-json@4.0.0: + resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} + engines: {node: '>=4'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse-path@7.0.0: + resolution: {integrity: sha512-Euf9GG8WT9CdqwuWJGdf3RkUcTBArppHABkO7Lm8IzRQp0e2r/kkFnmhu4TSK30Wcu5rVAZLmfPKSBBi9tWFog==} + + parse-url@8.1.0: + resolution: {integrity: sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w==} + + parse5@7.3.0: + resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==} + + parseurl@1.3.3: + resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} + engines: {node: '>= 0.8'} + + pascal-case@3.1.2: + resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} + + pascalcase@0.1.1: + resolution: {integrity: sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==} + engines: {node: '>=0.10.0'} + + path-browserify@0.0.1: + resolution: {integrity: sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==} + + path-dirname@1.0.2: + resolution: {integrity: sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==} + + path-exists@3.0.0: + resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} + engines: {node: '>=4'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-key@4.0.0: + resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} + engines: {node: '>=12'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + path-type@5.0.0: + resolution: {integrity: sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==} + engines: {node: '>=12'} + + pathe@1.1.1: + resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==} + + pathe@1.1.2: + resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} + + pbkdf2@3.1.2: + resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} + engines: {node: '>=0.12'} + + perfect-debounce@1.0.0: + resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} + + picocolors@0.2.1: + resolution: {integrity: sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==} + + picocolors@1.0.0: + resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + pidtree@0.6.0: + resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==} + engines: {node: '>=0.10'} + hasBin: true + + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pify@3.0.0: + resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} + engines: {node: '>=4'} + + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + + pify@5.0.0: + resolution: {integrity: sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==} + engines: {node: '>=10'} + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pkg-dir@3.0.0: + resolution: {integrity: sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==} + engines: {node: '>=6'} + + pkg-dir@4.2.0: + resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} + engines: {node: '>=8'} + + pkg-types@1.1.2: + resolution: {integrity: sha512-VEGf1he2DR5yowYRl0XJhWJq5ktm9gYIsH+y8sNJpHlxch7JPDaufgrsl4vYjd9hMUY8QVjoNncKbow9I7exyA==} + + pkg-types@1.2.1: + resolution: {integrity: sha512-sQoqa8alT3nHjGuTjuKgOnvjo4cljkufdtLMnO2LBP/wRwuDlo1tkaEdMxCRhyGRPacv/ztlZgDPm2b7FAmEvw==} + + pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + + pngjs@5.0.0: + resolution: {integrity: sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==} + engines: {node: '>=10.13.0'} + + pnp-webpack-plugin@1.7.0: + resolution: {integrity: sha512-2Rb3vm+EXble/sMXNSu6eoBx8e79gKqhNq9F5ZWW6ERNCTE/Q0wQNne5541tE5vKjfM8hpNCYL+LGc1YTfI0dg==} + engines: {node: '>=6'} + + posix-character-classes@0.1.1: + resolution: {integrity: sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==} + engines: {node: '>=0.10.0'} + + postcss-attribute-case-insensitive@6.0.3: + resolution: {integrity: sha512-KHkmCILThWBRtg+Jn1owTnHPnFit4OkqS+eKiGEOPIGke54DCeYGJ6r0Fx/HjfE9M9kznApCLcU0DvnPchazMQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + + postcss-calc@10.0.0: + resolution: {integrity: sha512-OmjhudoNTP0QleZCwl1i6NeBwN+5MZbY5ersLZz69mjJiDVv/p57RjRuKDkHeDWr4T+S97wQfsqRTNoDHB2e3g==} + engines: {node: ^18.12 || ^20.9 || >=22.0} + peerDependencies: + postcss: ^8.4.38 + + postcss-calc@8.2.4: + resolution: {integrity: sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==} + peerDependencies: + postcss: ^8.2.2 + + postcss-clamp@4.1.0: + resolution: {integrity: sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==} + engines: {node: '>=7.6.0'} + peerDependencies: + postcss: ^8.4.6 + + postcss-color-functional-notation@6.0.12: + resolution: {integrity: sha512-LGLWl6EDofJwDHMElYvt4YU9AeH+oijzOfeKhE0ebuu0aBSDeEg7CfFXMi0iiXWV1VKxn3MLGOtcBNnOiQS9Yg==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + + postcss-color-hex-alpha@9.0.4: + resolution: {integrity: sha512-XQZm4q4fNFqVCYMGPiBjcqDhuG7Ey2xrl99AnDJMyr5eDASsAGalndVgHZF8i97VFNy1GQeZc4q2ydagGmhelQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + + postcss-color-rebeccapurple@9.0.3: + resolution: {integrity: sha512-ruBqzEFDYHrcVq3FnW3XHgwRqVMrtEPLBtD7K2YmsLKVc2jbkxzzNEctJKsPCpDZ+LeMHLKRDoSShVefGc+CkQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + + postcss-colormin@5.3.1: + resolution: {integrity: sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-colormin@7.0.1: + resolution: {integrity: sha512-uszdT0dULt3FQs47G5UHCduYK+FnkLYlpu1HpWu061eGsKZ7setoG7kA+WC9NQLsOJf69D5TxGHgnAdRgylnFQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-convert-values@5.1.3: + resolution: {integrity: sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-convert-values@7.0.1: + resolution: {integrity: sha512-9x2ofb+hYPwHWMlWAzyWys2yMDZYGfkX9LodbaVTmLdlupmtH2AGvj8Up95wzzNPRDEzPIxQIkUaPJew3bT6xA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-custom-media@10.0.7: + resolution: {integrity: sha512-o2k5nnvRZhF36pr1fGFM7a1EMTcNdKNO70Tp1g2lfpYgiwIctR7ic4acBCDHBMYRcQ8mFlaBB1QsEywqrSIaFQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + + postcss-custom-properties@13.3.11: + resolution: {integrity: sha512-CAIgz03I/GMhVbAKIi3u3P8j5JY2KHl0TlePcfUX3OUy8t0ynnWvyJaS1D92pEAw1LjmeKWi7+aIU0s53iYdOQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + + postcss-custom-selectors@7.1.11: + resolution: {integrity: sha512-IoGprXOueDJL5t3ZuWR+QzPpmrQCFNhvoICsg0vDSehGwWNG0YV/Z4A+zouGRonC7NJThoV+A8A74IEMqMQUQw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + + postcss-dir-pseudo-class@8.0.1: + resolution: {integrity: sha512-uULohfWBBVoFiZXgsQA24JV6FdKIidQ+ZqxOouhWwdE+qJlALbkS5ScB43ZTjPK+xUZZhlaO/NjfCt5h4IKUfw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + + postcss-discard-comments@5.1.2: + resolution: {integrity: sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-discard-comments@7.0.1: + resolution: {integrity: sha512-GVrQxUOhmle1W6jX2SvNLt4kmN+JYhV7mzI6BMnkAWR9DtVvg8e67rrV0NfdWhn7x1zxvzdWkMBPdBDCls+uwQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-discard-duplicates@5.1.0: + resolution: {integrity: sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-discard-duplicates@7.0.0: + resolution: {integrity: sha512-bAnSuBop5LpAIUmmOSsuvtKAAKREB6BBIYStWUTGq8oG5q9fClDMMuY8i4UPI/cEcDx2TN+7PMnXYIId20UVDw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-discard-empty@5.1.1: + resolution: {integrity: sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-discard-empty@7.0.0: + resolution: {integrity: sha512-e+QzoReTZ8IAwhnSdp/++7gBZ/F+nBq9y6PomfwORfP7q9nBpK5AMP64kOt0bA+lShBFbBDcgpJ3X4etHg4lzA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-discard-overridden@5.1.0: + resolution: {integrity: sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-discard-overridden@7.0.0: + resolution: {integrity: sha512-GmNAzx88u3k2+sBTZrJSDauR0ccpE24omTQCVmaTTZFz1du6AasspjaUPMJ2ud4RslZpoFKyf+6MSPETLojc6w==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-double-position-gradients@5.0.6: + resolution: {integrity: sha512-QJ+089FKMaqDxOhhIHsJrh4IP7h4PIHNC5jZP5PMmnfUScNu8Hji2lskqpFWCvu+5sj+2EJFyzKd13sLEWOZmQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + + postcss-focus-visible@9.0.1: + resolution: {integrity: sha512-N2VQ5uPz3Z9ZcqI5tmeholn4d+1H14fKXszpjogZIrFbhaq0zNAtq8sAnw6VLiqGbL8YBzsnu7K9bBkTqaRimQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + + postcss-focus-within@8.0.1: + resolution: {integrity: sha512-NFU3xcY/xwNaapVb+1uJ4n23XImoC86JNwkY/uduytSl2s9Ekc2EpzmRR63+ExitnW3Mab3Fba/wRPCT5oDILA==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + + postcss-font-variant@5.0.0: + resolution: {integrity: sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==} + peerDependencies: + postcss: ^8.1.0 + + postcss-gap-properties@5.0.1: + resolution: {integrity: sha512-k2z9Cnngc24c0KF4MtMuDdToROYqGMMUQGcE6V0odwjHyOHtaDBlLeRBV70y9/vF7KIbShrTRZ70JjsI1BZyWw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + + postcss-html@1.8.1: + resolution: {integrity: sha512-OLF6P7qctfAWayOhLpcVnTGqVeJzu2W3WpIYelfz2+JV5oGxfkcEvweN9U4XpeqE0P98dcD9ssusGwlF0TK0uQ==} + engines: {node: ^12 || >=14} + + postcss-image-set-function@6.0.3: + resolution: {integrity: sha512-i2bXrBYzfbRzFnm+pVuxVePSTCRiNmlfssGI4H0tJQvDue+yywXwUxe68VyzXs7cGtMaH6MCLY6IbCShrSroCw==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + + postcss-import-resolver@2.0.0: + resolution: {integrity: sha512-y001XYgGvVwgxyxw9J1a5kqM/vtmIQGzx34g0A0Oy44MFcy/ZboZw1hu/iN3VYFjSTRzbvd7zZJJz0Kh0AGkTw==} + + postcss-import@15.1.0: + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-lab-function@6.0.17: + resolution: {integrity: sha512-QzjC6/3J6XKZzHGuUKhWNvlDMfWo+08dQOfQj4vWQdpZFdOxCh9QCR4w4XbV68EkdzywJie1mcm81jwFyV0+kg==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + + postcss-loader@4.3.0: + resolution: {integrity: sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q==} + engines: {node: '>= 10.13.0'} + peerDependencies: + postcss: ^7.0.0 || ^8.0.1 + webpack: ^4.0.0 || ^5.0.0 + + postcss-logical@7.0.1: + resolution: {integrity: sha512-8GwUQZE0ri0K0HJHkDv87XOLC8DE0msc+HoWLeKdtjDZEwpZ5xuK3QdV6FhmHSQW40LPkg43QzvATRAI3LsRkg==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + + postcss-merge-longhand@5.1.7: + resolution: {integrity: sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-merge-longhand@7.0.2: + resolution: {integrity: sha512-06vrW6ZWi9qeP7KMS9fsa9QW56+tIMW55KYqF7X3Ccn+NI2pIgPV6gFfvXTMQ05H90Y5DvnCDPZ2IuHa30PMUg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-merge-rules@5.1.4: + resolution: {integrity: sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-merge-rules@7.0.2: + resolution: {integrity: sha512-VAR47UNvRsdrTHLe7TV1CeEtF9SJYR5ukIB9U4GZyZOptgtsS20xSxy+k5wMrI3udST6O1XuIn7cjQkg7sDAAw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-minify-font-values@5.1.0: + resolution: {integrity: sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-minify-font-values@7.0.0: + resolution: {integrity: sha512-2ckkZtgT0zG8SMc5aoNwtm5234eUx1GGFJKf2b1bSp8UflqaeFzR50lid4PfqVI9NtGqJ2J4Y7fwvnP/u1cQog==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-minify-gradients@5.1.1: + resolution: {integrity: sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-minify-gradients@7.0.0: + resolution: {integrity: sha512-pdUIIdj/C93ryCHew0UgBnL2DtUS3hfFa5XtERrs4x+hmpMYGhbzo6l/Ir5de41O0GaKVpK1ZbDNXSY6GkXvtg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-minify-params@5.1.4: + resolution: {integrity: sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-minify-params@7.0.1: + resolution: {integrity: sha512-e+Xt8xErSRPgSRFxHeBCSxMiO8B8xng7lh8E0A5ep1VfwYhY8FXhu4Q3APMjgx9YDDbSp53IBGENrzygbUvgUQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-minify-selectors@5.2.1: + resolution: {integrity: sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-minify-selectors@7.0.2: + resolution: {integrity: sha512-dCzm04wqW1uqLmDZ41XYNBJfjgps3ZugDpogAmJXoCb5oCiTzIX4oPXXKxDpTvWOnKxQKR4EbV4ZawJBLcdXXA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-modules-extract-imports@3.0.0: + resolution: {integrity: sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-local-by-default@4.0.3: + resolution: {integrity: sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-scope@3.0.0: + resolution: {integrity: sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-modules-values@4.0.0: + resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==} + engines: {node: ^10 || ^12 || >= 14} + peerDependencies: + postcss: ^8.1.0 + + postcss-nesting@12.1.5: + resolution: {integrity: sha512-N1NgI1PDCiAGWPTYrwqm8wpjv0bgDmkYHH72pNsqTCv9CObxjxftdYu6AKtGN+pnJa7FQjMm3v4sp8QJbFsYdQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + + postcss-normalize-charset@5.1.0: + resolution: {integrity: sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-charset@7.0.0: + resolution: {integrity: sha512-ABisNUXMeZeDNzCQxPxBCkXexvBrUHV+p7/BXOY+ulxkcjUZO0cp8ekGBwvIh2LbCwnWbyMPNJVtBSdyhM2zYQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-display-values@5.1.0: + resolution: {integrity: sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-display-values@7.0.0: + resolution: {integrity: sha512-lnFZzNPeDf5uGMPYgGOw7v0BfB45+irSRz9gHQStdkkhiM0gTfvWkWB5BMxpn0OqgOQuZG/mRlZyJxp0EImr2Q==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-positions@5.1.1: + resolution: {integrity: sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-positions@7.0.0: + resolution: {integrity: sha512-I0yt8wX529UKIGs2y/9Ybs2CelSvItfmvg/DBIjTnoUSrPxSV7Z0yZ8ShSVtKNaV/wAY+m7bgtyVQLhB00A1NQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-repeat-style@5.1.1: + resolution: {integrity: sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-repeat-style@7.0.0: + resolution: {integrity: sha512-o3uSGYH+2q30ieM3ppu9GTjSXIzOrRdCUn8UOMGNw7Af61bmurHTWI87hRybrP6xDHvOe5WlAj3XzN6vEO8jLw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-string@5.1.0: + resolution: {integrity: sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-string@7.0.0: + resolution: {integrity: sha512-w/qzL212DFVOpMy3UGyxrND+Kb0fvCiBBujiaONIihq7VvtC7bswjWgKQU/w4VcRyDD8gpfqUiBQ4DUOwEJ6Qg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-timing-functions@5.1.0: + resolution: {integrity: sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-timing-functions@7.0.0: + resolution: {integrity: sha512-tNgw3YV0LYoRwg43N3lTe3AEWZ66W7Dh7lVEpJbHoKOuHc1sLrzMLMFjP8SNULHaykzsonUEDbKedv8C+7ej6g==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-unicode@5.1.1: + resolution: {integrity: sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-unicode@7.0.1: + resolution: {integrity: sha512-PTPGdY9xAkTw+8ZZ71DUePb7M/Vtgkbbq+EoI33EuyQEzbKemEQMhe5QSr0VP5UfZlreANDPxSfcdSprENcbsg==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-url@5.1.0: + resolution: {integrity: sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-url@7.0.0: + resolution: {integrity: sha512-+d7+PpE+jyPX1hDQZYG+NaFD+Nd2ris6r8fPTBAjE8z/U41n/bib3vze8x7rKs5H1uEw5ppe9IojewouHk0klQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-normalize-whitespace@5.1.1: + resolution: {integrity: sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-normalize-whitespace@7.0.0: + resolution: {integrity: sha512-37/toN4wwZErqohedXYqWgvcHUGlT8O/m2jVkAfAe9Bd4MzRqlBmXrJRePH0e9Wgnz2X7KymTgTOaaFizQe3AQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-opacity-percentage@2.0.0: + resolution: {integrity: sha512-lyDrCOtntq5Y1JZpBFzIWm2wG9kbEdujpNt4NLannF+J9c8CgFIzPa80YQfdza+Y+yFfzbYj/rfoOsYsooUWTQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.2 + + postcss-ordered-values@5.1.3: + resolution: {integrity: sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-ordered-values@7.0.1: + resolution: {integrity: sha512-irWScWRL6nRzYmBOXReIKch75RRhNS86UPUAxXdmW/l0FcAsg0lvAXQCby/1lymxn/o0gVa6Rv/0f03eJOwHxw==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-overflow-shorthand@5.0.1: + resolution: {integrity: sha512-XzjBYKLd1t6vHsaokMV9URBt2EwC9a7nDhpQpjoPk2HRTSQfokPfyAS/Q7AOrzUu6q+vp/GnrDBGuj/FCaRqrQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + + postcss-page-break@3.0.4: + resolution: {integrity: sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==} + peerDependencies: + postcss: ^8 + + postcss-place@9.0.1: + resolution: {integrity: sha512-JfL+paQOgRQRMoYFc2f73pGuG/Aw3tt4vYMR6UA3cWVMxivviPTnMFnFTczUJOA4K2Zga6xgQVE+PcLs64WC8Q==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + + postcss-preset-env@9.5.15: + resolution: {integrity: sha512-z/2akOVQChOGAdzaUR4pQrDOM3xGZc5/k4THHWyREbWAfngaJATA2SkEQMkiyV5Y/EoSwE0nt0IiaIs6CMmxfQ==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + + postcss-pseudo-class-any-link@9.0.2: + resolution: {integrity: sha512-HFSsxIqQ9nA27ahyfH37cRWGk3SYyQLpk0LiWw/UGMV4VKT5YG2ONee4Pz/oFesnK0dn2AjcyequDbIjKJgB0g==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + + postcss-reduce-initial@5.1.2: + resolution: {integrity: sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-reduce-initial@7.0.1: + resolution: {integrity: sha512-0JDUSV4bGB5FGM5g8MkS+rvqKukJZ7OTHw/lcKn7xPNqeaqJyQbUO8/dJpvyTpaVwPsd3Uc33+CfNzdVowp2WA==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-reduce-transforms@5.1.0: + resolution: {integrity: sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-reduce-transforms@7.0.0: + resolution: {integrity: sha512-pnt1HKKZ07/idH8cpATX/ujMbtOGhUfE+m8gbqwJE05aTaNw8gbo34a2e3if0xc0dlu75sUOiqvwCGY3fzOHew==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-replace-overflow-wrap@4.0.0: + resolution: {integrity: sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==} + peerDependencies: + postcss: ^8.0.3 + + postcss-resolve-nested-selector@0.1.1: + resolution: {integrity: sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==} + + postcss-safe-parser@6.0.0: + resolution: {integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.3.3 + + postcss-selector-not@7.0.2: + resolution: {integrity: sha512-/SSxf/90Obye49VZIfc0ls4H0P6i6V1iHv0pzZH8SdgvZOPFkF37ef1r5cyWcMflJSFJ5bfuoluTnFnBBFiuSA==} + engines: {node: ^14 || ^16 || >=18} + peerDependencies: + postcss: ^8.4 + + postcss-selector-parser@6.0.13: + resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==} + engines: {node: '>=4'} + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-svgo@5.1.0: + resolution: {integrity: sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-svgo@7.0.1: + resolution: {integrity: sha512-0WBUlSL4lhD9rA5k1e5D8EN5wCEyZD6HJk0jIvRxl+FDVOMlJ7DePHYWGGVc5QRqrJ3/06FTXM0bxjmJpmTPSA==} + engines: {node: ^18.12.0 || ^20.9.0 || >= 18} + peerDependencies: + postcss: ^8.4.31 + + postcss-unique-selectors@5.1.1: + resolution: {integrity: sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + postcss-unique-selectors@7.0.1: + resolution: {integrity: sha512-MH7QE/eKUftTB5ta40xcHLl7hkZjgDFydpfTK+QWXeHxghVt3VoPqYL5/G+zYZPPIs+8GuqFXSTgxBSoB1RZtQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + postcss-url@10.1.3: + resolution: {integrity: sha512-FUzyxfI5l2tKmXdYc6VTu3TWZsInayEKPbiyW+P6vmmIrrb4I6CGX0BFoewgYHLK+oIL5FECEK02REYRpBvUCw==} + engines: {node: '>=10'} + peerDependencies: + postcss: ^8.0.0 + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@7.0.39: + resolution: {integrity: sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==} + engines: {node: '>=6.0.0'} + + postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} + + postcss@8.5.10: + resolution: {integrity: sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==} + engines: {node: ^10 || ^12 || >=14} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prepend-http@1.0.4: + resolution: {integrity: sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==} + engines: {node: '>=0.10.0'} + + prettier@2.8.8: + resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==} + engines: {node: '>=10.13.0'} + hasBin: true + + prettier@3.8.1: + resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} + engines: {node: '>=14'} + hasBin: true + + pretty-bytes@5.6.0: + resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} + engines: {node: '>=6'} + + pretty-error@2.1.2: + resolution: {integrity: sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==} + + pretty-format@30.2.0: + resolution: {integrity: sha512-9uBdv/B4EefsuAL+pWqueZyZS2Ba+LxfFeQ9DN14HU4bN8bhaxKdkpjpB6fs9+pSjIBu+FXQHImEg8j/Lw0+vA==} + engines: {node: ^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0} + + pretty-time@1.1.0: + resolution: {integrity: sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==} + engines: {node: '>=4'} + + pretty@2.0.0: + resolution: {integrity: sha512-G9xUchgTEiNpormdYBl+Pha50gOUovT18IvAe7EYMZ1/f9W/WWMPRn+xI68yXNMUk3QXHDwo/1wV/4NejVNe1w==} + engines: {node: '>=0.10.0'} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + + promise-inflight@1.0.1: + resolution: {integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==} + peerDependencies: + bluebird: '*' + peerDependenciesMeta: + bluebird: + optional: true + + proper-lockfile@4.1.2: + resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} + + proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + + proto3-json-serializer@0.1.9: + resolution: {integrity: sha512-A60IisqvnuI45qNRygJjrnNjX2TMdQGMY+57tR3nul3ZgO2zXkR9OGR8AXxJhkqx84g0FTnrfi3D5fWMSdANdQ==} + + protobufjs@6.11.3: + resolution: {integrity: sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==} + hasBin: true + + protobufjs@6.11.4: + resolution: {integrity: sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==} + hasBin: true + + protobufjs@7.2.5: + resolution: {integrity: sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==} + engines: {node: '>=12.0.0'} + + protocols@2.0.1: + resolution: {integrity: sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + prr@1.0.1: + resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + + pseudomap@1.0.2: + resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==} + + public-encrypt@4.0.3: + resolution: {integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==} + + pump@2.0.1: + resolution: {integrity: sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==} + + pump@3.0.0: + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + + pumpify@1.5.1: + resolution: {integrity: sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==} + + pumpify@2.0.1: + resolution: {integrity: sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==} + + punycode@1.4.1: + resolution: {integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==} + + punycode@2.3.0: + resolution: {integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==} + engines: {node: '>=6'} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + pure-rand@7.0.1: + resolution: {integrity: sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ==} + + pusher-js@8.4.0: + resolution: {integrity: sha512-wp3HqIIUc1GRyu1XrP6m2dgyE9MoCsXVsWNlohj0rjSkLf+a0jLvEyVubdg58oMk7bhjBWnFClgp8jfAa6Ak4Q==} + + qrcode@1.5.4: + resolution: {integrity: sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==} + engines: {node: '>=10.13.0'} + hasBin: true + + qs@6.11.2: + resolution: {integrity: sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==} + engines: {node: '>=0.6'} + + query-string@4.3.4: + resolution: {integrity: sha512-O2XLNDBIg1DnTOa+2XrIwSiXEV8h2KImXUnjhhn2+UsvZ+Es2uyd5CCRTNQlDGbzUQOW3aYCBx9rVA6dzsiY7Q==} + engines: {node: '>=0.10.0'} + + querystring-es3@0.2.1: + resolution: {integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==} + engines: {node: '>=0.4.x'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + + randomfill@1.0.4: + resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==} + + range-parser@1.2.1: + resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} + engines: {node: '>= 0.6'} + + rc9@2.1.1: + resolution: {integrity: sha512-lNeOl38Ws0eNxpO3+wD1I9rkHGQyj1NU1jlzv4go2CtEnEQEUfqnIvZG7W+bC/aXdJ27n5x/yUjb6RoT9tko+Q==} + + rc9@2.1.2: + resolution: {integrity: sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + + read-pkg-up@7.0.1: + resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==} + engines: {node: '>=8'} + + read-pkg-up@8.0.0: + resolution: {integrity: sha512-snVCqPczksT0HS2EC+SxUndvSzn6LRCwpfSvLrIfR5BKDQQZMaI6jPRC9dYvYFDRAuFEAnkwww8kBBNE/3VvzQ==} + engines: {node: '>=12'} + + read-pkg@5.2.0: + resolution: {integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==} + engines: {node: '>=8'} + + read-pkg@6.0.0: + resolution: {integrity: sha512-X1Fu3dPuk/8ZLsMhEj5f4wFAF0DWoK7qhGJvgaijocXxBmSToKfbFtqbxMO7bVjNA1dmE5huAzjXj/ey86iw9Q==} + engines: {node: '>=12'} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdirp@2.2.1: + resolution: {integrity: sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==} + engines: {node: '>=0.10'} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + redent@4.0.0: + resolution: {integrity: sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==} + engines: {node: '>=12'} + + regenerate-unicode-properties@10.1.1: + resolution: {integrity: sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==} + engines: {node: '>=4'} + + regenerate@1.4.2: + resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==} + + regenerator-runtime@0.11.1: + resolution: {integrity: sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==} + + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + + regenerator-transform@0.15.2: + resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==} + + regex-not@1.0.2: + resolution: {integrity: sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==} + engines: {node: '>=0.10.0'} + + regexp-tree@0.1.27: + resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} + hasBin: true + + regexp.prototype.flags@1.5.1: + resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==} + engines: {node: '>= 0.4'} + + regexpp@3.2.0: + resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==} + engines: {node: '>=8'} + + regexpu-core@5.3.2: + resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==} + engines: {node: '>=4'} + + regjsparser@0.9.1: + resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==} + hasBin: true + + relateurl@0.2.7: + resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} + engines: {node: '>= 0.10'} + + remove-trailing-separator@1.1.0: + resolution: {integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==} + + renderkid@2.0.7: + resolution: {integrity: sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ==} + + repeat-element@1.1.4: + resolution: {integrity: sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==} + engines: {node: '>=0.10.0'} + + repeat-string@1.6.1: + resolution: {integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==} + engines: {node: '>=0.10'} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + require-from-string@2.0.2: + resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==} + engines: {node: '>=0.10.0'} + + require-main-filename@2.0.0: + resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==} + + resolve-cwd@3.0.0: + resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} + engines: {node: '>=8'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve-url@0.2.1: + resolution: {integrity: sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==} + deprecated: https://github.com/lydell/resolve-url#deprecated + + resolve@1.22.6: + resolution: {integrity: sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==} + hasBin: true + + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + ret@0.1.15: + resolution: {integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==} + engines: {node: '>=0.12'} + + retry-request@4.2.2: + resolution: {integrity: sha512-xA93uxUD/rogV7BV59agW/JHPGXeREMWiZc9jhcwY4YdZ7QOtC7qbomYg0n4wyk2lJhggjvKvhNX8wln/Aldhg==} + engines: {node: '>=8.10.0'} + + retry@0.12.0: + resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} + engines: {node: '>= 4'} + + retry@0.13.1: + resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} + engines: {node: '>= 4'} + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rfdc@1.4.1: + resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} + + rimraf@2.7.1: + resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + rimraf@3.0.2: + resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported + hasBin: true + + ripemd160@2.0.2: + resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} + + rollup@2.79.2: + resolution: {integrity: sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==} + engines: {node: '>=10.0.0'} + hasBin: true + + rollup@3.30.0: + resolution: {integrity: sha512-kQvGasUgN+AlWGliFn2POSajRQEsULVYFGTvOZmK06d7vCD+YhZztt70kGk3qaeAXeWYL5eO7zx+rAubBc55eA==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + + rrweb-cssom@0.8.0: + resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==} + + run-async@2.4.1: + resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} + engines: {node: '>=0.12.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + run-queue@1.0.3: + resolution: {integrity: sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==} + + rxjs@6.6.7: + resolution: {integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==} + engines: {npm: '>=2.0.0'} + + safe-array-concat@1.0.1: + resolution: {integrity: sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==} + engines: {node: '>=0.4'} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-regex-test@1.0.0: + resolution: {integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==} + + safe-regex@1.1.0: + resolution: {integrity: sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==} + + safe-regex@2.1.1: + resolution: {integrity: sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sass-loader@10.4.1: + resolution: {integrity: sha512-aX/iJZTTpNUNx/OSYzo2KsjIUQHqvWsAhhUijFjAPdZTEhstjZI9zTNvkTTwsx+uNUJqUwOw5gacxQMx4hJxGQ==} + engines: {node: '>= 10.13.0'} + peerDependencies: + fibers: '>= 3.1.0' + node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + sass: ^1.3.0 + webpack: ^4.36.0 || ^5.0.0 + peerDependenciesMeta: + fibers: + optional: true + node-sass: + optional: true + sass: + optional: true + + sass@1.32.13: + resolution: {integrity: sha512-dEgI9nShraqP7cXQH+lEXVf73WOPCse0QlFzSD8k+1TcOxCMwVXfQlr0jtoluZysQOyJGnfr21dLvYKDJq8HkA==} + engines: {node: '>=8.9.0'} + hasBin: true + + sax@1.4.1: + resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} + + saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + + schema-utils@1.0.0: + resolution: {integrity: sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==} + engines: {node: '>= 4'} + + schema-utils@2.7.0: + resolution: {integrity: sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==} + engines: {node: '>= 8.9.0'} + + schema-utils@2.7.1: + resolution: {integrity: sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==} + engines: {node: '>= 8.9.0'} + + schema-utils@3.3.0: + resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} + engines: {node: '>= 10.13.0'} + + schema-utils@4.3.3: + resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} + engines: {node: '>= 10.13.0'} + + scule@0.2.1: + resolution: {integrity: sha512-M9gnWtn3J0W+UhJOHmBxBTwv8mZCan5i1Himp60t6vvZcor0wr+IM0URKmIglsWJ7bRujNAVVN77fp+uZaWoKg==} + + scule@1.0.0: + resolution: {integrity: sha512-4AsO/FrViE/iDNEPaAQlb77tf0csuq27EsVpy6ett584EcRTp6pTDLoGWVxCD77y5iU5FauOvhsI4o1APwPoSQ==} + + scule@1.3.0: + resolution: {integrity: sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==} + + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + + send@0.19.0: + resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} + engines: {node: '>= 0.8.0'} + + serialize-javascript@4.0.0: + resolution: {integrity: sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==} + + serialize-javascript@5.0.1: + resolution: {integrity: sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==} + + serialize-javascript@6.0.1: + resolution: {integrity: sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==} + + serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + + serve-placeholder@2.0.2: + resolution: {integrity: sha512-/TMG8SboeiQbZJWRlfTCqMs2DD3SZgWp0kDQePz9yUuCnDfDh/92gf7/PxGhzXTKBIPASIHxFcZndoNbp6QOLQ==} + + serve-static@1.16.2: + resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} + engines: {node: '>= 0.8.0'} + + server-destroy@1.0.1: + resolution: {integrity: sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==} + + set-blocking@2.0.0: + resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} + + set-function-name@2.0.1: + resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==} + engines: {node: '>= 0.4'} + + set-value@2.0.1: + resolution: {integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==} + engines: {node: '>=0.10.0'} + + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + + setprototypeof@1.2.0: + resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + + sha.js@2.4.11: + resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} + hasBin: true + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.8.1: + resolution: {integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==} + + side-channel@1.0.4: + resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + sirv@2.0.3: + resolution: {integrity: sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==} + engines: {node: '>= 10'} + + sitemap@4.1.1: + resolution: {integrity: sha512-+8yd66IxyIFEMFkFpVoPuoPwBvdiL7Ap/HS5YD7igqO4phkyTPFIprCAE9NMHehAY5ZGN3MkAze4lDrOAX3sVQ==} + engines: {node: '>=8.9.0', npm: '>=5.6.0'} + hasBin: true + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + slash@4.0.0: + resolution: {integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==} + engines: {node: '>=12'} + + slash@5.1.0: + resolution: {integrity: sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==} + engines: {node: '>=14.16'} + + slice-ansi@4.0.0: + resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==} + engines: {node: '>=10'} + + slice-ansi@5.0.0: + resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==} + engines: {node: '>=12'} + + slice-ansi@7.1.0: + resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} + engines: {node: '>=18'} + + snapdragon-node@2.1.1: + resolution: {integrity: sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==} + engines: {node: '>=0.10.0'} + + snapdragon-util@3.0.1: + resolution: {integrity: sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==} + engines: {node: '>=0.10.0'} + + snapdragon@0.8.2: + resolution: {integrity: sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==} + engines: {node: '>=0.10.0'} + + sort-keys@1.1.2: + resolution: {integrity: sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==} + engines: {node: '>=0.10.0'} + + sort-keys@2.0.0: + resolution: {integrity: sha512-/dPCrG1s3ePpWm6yBbxZq5Be1dXGLyLn9Z791chDC3NFrpkVbWGzkBwPN1knaciexFXgRJ7hzdnwZ4stHSDmjg==} + engines: {node: '>=4'} + + source-list-map@2.0.1: + resolution: {integrity: sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==} + + source-map-js@1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map-resolve@0.5.3: + resolution: {integrity: sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==} + deprecated: See https://github.com/lydell/source-map-resolve#deprecated + + source-map-support@0.5.13: + resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} + + source-map-support@0.5.21: + resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} + + source-map-url@0.4.1: + resolution: {integrity: sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==} + deprecated: See https://github.com/lydell/source-map-url#deprecated + + source-map@0.5.6: + resolution: {integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==} + engines: {node: '>=0.10.0'} + + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + + spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + + spdx-exceptions@2.3.0: + resolution: {integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==} + + spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + + spdx-license-ids@3.0.15: + resolution: {integrity: sha512-lpT8hSQp9jAKp9mhtBU4Xjon8LPGBvLIuBiSVhMEtmLecTh2mO0tlqrAMp47tBXzMr13NJMQ2lf7RpQGLJ3HsQ==} + + split-string@3.1.0: + resolution: {integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==} + engines: {node: '>=0.10.0'} + + split2@4.2.0: + resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} + engines: {node: '>= 10.x'} + + sprintf-js@1.0.3: + resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} + + ssri@6.0.2: + resolution: {integrity: sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==} + + ssri@8.0.1: + resolution: {integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==} + engines: {node: '>= 8'} + + stable@0.1.8: + resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} + deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' + + stack-trace@0.0.10: + resolution: {integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==} + + stack-utils@2.0.6: + resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} + engines: {node: '>=10'} + + stackframe@1.3.4: + resolution: {integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==} + + static-extend@0.1.2: + resolution: {integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==} + engines: {node: '>=0.10.0'} + + statuses@1.5.0: + resolution: {integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==} + engines: {node: '>= 0.6'} + + statuses@2.0.1: + resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} + engines: {node: '>= 0.8'} + + std-env@3.7.0: + resolution: {integrity: sha512-JPbdCEQLj1w5GilpiHAx3qJvFndqybBysA3qUOnznweH4QbNYUsW/ea8QzSrnh0vNsezMMw5bcVool8lM0gwzg==} + + stream-browserify@2.0.2: + resolution: {integrity: sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==} + + stream-each@1.2.3: + resolution: {integrity: sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==} + + stream-events@1.0.5: + resolution: {integrity: sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==} + + stream-http@2.8.3: + resolution: {integrity: sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==} + + stream-shift@1.0.1: + resolution: {integrity: sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==} + + strict-uri-encode@1.1.0: + resolution: {integrity: sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==} + engines: {node: '>=0.10.0'} + + string-argv@0.3.2: + resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} + engines: {node: '>=0.6.19'} + + string-length@4.0.2: + resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==} + engines: {node: '>=10'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string.prototype.trim@1.2.8: + resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.7: + resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==} + + string.prototype.trimstart@1.0.7: + resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@3.0.1: + resolution: {integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==} + engines: {node: '>=0.10.0'} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-ansi@7.1.2: + resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-bom@4.0.0: + resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==} + engines: {node: '>=8'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-final-newline@3.0.0: + resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} + engines: {node: '>=12'} + + strip-indent@3.0.0: + resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} + engines: {node: '>=8'} + + strip-indent@4.0.0: + resolution: {integrity: sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==} + engines: {node: '>=12'} + + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + strip-literal@1.3.0: + resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} + + strip-literal@2.1.0: + resolution: {integrity: sha512-Op+UycaUt/8FbN/Z2TWPBLge3jWrP3xj10f3fnYxf052bKuS3EKs1ZQcVGjnEMdsNVAM+plXRdmjrZ/KgG3Skw==} + + stubs@3.0.0: + resolution: {integrity: sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==} + + style-resources-loader@1.5.0: + resolution: {integrity: sha512-fIfyvQ+uvXaCBGGAgfh+9v46ARQB1AWdaop2RpQw0PBVuROsTBqGvx8dj0kxwjGOAyq3vepe4AOK3M6+Q/q2jw==} + engines: {node: '>=8.9'} + peerDependencies: + webpack: ^3.0.0 || ^4.0.0 || ^5.0.0 + + style-search@0.1.0: + resolution: {integrity: sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==} + + stylehacks@5.1.1: + resolution: {integrity: sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==} + engines: {node: ^10 || ^12 || >=14.0} + peerDependencies: + postcss: ^8.2.15 + + stylehacks@7.0.2: + resolution: {integrity: sha512-HdkWZS9b4gbgYTdMg4gJLmm7biAUug1qTqXjS+u8X+/pUd+9Px1E+520GnOW3rST9MNsVOVpsJG+mPHNosxjOQ==} + engines: {node: ^18.12.0 || ^20.9.0 || >=22.0} + peerDependencies: + postcss: ^8.4.31 + + stylelint-config-html@1.1.0: + resolution: {integrity: sha512-IZv4IVESjKLumUGi+HWeb7skgO6/g4VMuAYrJdlqQFndgbj6WJAXPhaysvBiXefX79upBdQVumgYcdd17gCpjQ==} + engines: {node: ^12 || >=14} + peerDependencies: + postcss-html: ^1.0.0 + stylelint: '>=14.0.0' + + stylelint-config-prettier@9.0.5: + resolution: {integrity: sha512-U44lELgLZhbAD/xy/vncZ2Pq8sh2TnpiPvo38Ifg9+zeioR+LAkHu0i6YORIOxFafZoVg0xqQwex6e6F25S5XA==} + engines: {node: '>= 12'} + hasBin: true + peerDependencies: + stylelint: '>= 11.x < 15' + + stylelint-config-recommended-vue@1.5.0: + resolution: {integrity: sha512-65TAK/clUqkNtkZLcuytoxU0URQYlml+30Nhop7sRkCZ/mtWdXt7T+spPSB3KMKlb+82aEVJ4OrcstyDBdbosg==} + engines: {node: ^12 || >=14} + peerDependencies: + postcss-html: ^1.0.0 + stylelint: '>=14.0.0' + + stylelint-config-recommended@13.0.0: + resolution: {integrity: sha512-EH+yRj6h3GAe/fRiyaoO2F9l9Tgg50AOFhaszyfov9v6ayXJ1IkSHwTxd7lB48FmOeSGDPLjatjO11fJpmarkQ==} + engines: {node: ^14.13.1 || >=16.0.0} + peerDependencies: + stylelint: ^15.10.0 + + stylelint-config-standard@34.0.0: + resolution: {integrity: sha512-u0VSZnVyW9VSryBG2LSO+OQTjN7zF9XJaAJRX/4EwkmU0R2jYwmBSN10acqZisDitS0CLiEiGjX7+Hrq8TAhfQ==} + engines: {node: ^14.13.1 || >=16.0.0} + peerDependencies: + stylelint: ^15.10.0 + + stylelint-webpack-plugin@5.0.1: + resolution: {integrity: sha512-07lpo1uVoFctKv0EOOg/YSrUppcLMjNBSMRqgooNnlbfAOgQfMzvLK+EbXz0HQiEgZobr+XQX9md/TgwTGdzbw==} + engines: {node: '>= 18.12.0'} + peerDependencies: + stylelint: ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + webpack: ^5.0.0 + + stylelint@15.11.0: + resolution: {integrity: sha512-78O4c6IswZ9TzpcIiQJIN49K3qNoXTM8zEJzhaTE/xRTCZswaovSEVIa/uwbOltZrk16X4jAxjaOhzz/hTm1Kw==} + engines: {node: ^14.13.1 || >=16.0.0} + hasBin: true + + supports-color@2.0.0: + resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==} + engines: {node: '>=0.8.0'} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-hyperlinks@3.0.0: + resolution: {integrity: sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==} + engines: {node: '>=14.18'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + svg-tags@1.0.0: + resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==} + + svgo@2.8.0: + resolution: {integrity: sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==} + engines: {node: '>=10.13.0'} + hasBin: true + + svgo@3.3.2: + resolution: {integrity: sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==} + engines: {node: '>=14.0.0'} + hasBin: true + + symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + + synckit@0.11.11: + resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==} + engines: {node: ^14.18.0 || >=16.0.0} + + table@6.8.1: + resolution: {integrity: sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==} + engines: {node: '>=10.0.0'} + + tapable@1.1.3: + resolution: {integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==} + engines: {node: '>=6'} + + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} + engines: {node: '>=6'} + + tar@6.2.0: + resolution: {integrity: sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==} + engines: {node: '>=10'} + deprecated: Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me + + teeny-request@7.2.0: + resolution: {integrity: sha512-SyY0pek1zWsi0LRVAALem+avzMLc33MKW/JLLakdP4s9+D7+jHcy5x6P+h94g2QNZsAqQNfX5lsbd3WSeJXrrw==} + engines: {node: '>=10'} + + terser-webpack-plugin@1.4.6: + resolution: {integrity: sha512-2lBVf/VMVIddjSn3GqbT90GvIJ/eYXJkt8cTzU7NbjKqK8fwv18Ftr4PlbF46b/e88743iZFL5Dtr/rC4hjIeA==} + engines: {node: '>= 6.9.0'} + peerDependencies: + webpack: ^4.0.0 + + terser-webpack-plugin@4.2.3: + resolution: {integrity: sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + + terser-webpack-plugin@5.3.16: + resolution: {integrity: sha512-h9oBFCWrq78NyWWVcSwZarJkZ01c2AyGrzs1crmHZO3QUg9D61Wu4NPjBy69n7JqylFF5y+CsUZYmYEIZ3mR+Q==} + engines: {node: '>= 10.13.0'} + peerDependencies: + '@swc/core': '*' + esbuild: '*' + uglify-js: '*' + webpack: ^5.1.0 + peerDependenciesMeta: + '@swc/core': + optional: true + esbuild: + optional: true + uglify-js: + optional: true + + terser@4.8.1: + resolution: {integrity: sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==} + engines: {node: '>=6.0.0'} + hasBin: true + + terser@5.44.1: + resolution: {integrity: sha512-t/R3R/n0MSwnnazuPpPNVO60LX0SKL45pyl9YlvxIdkH0Of7D5qM2EVe+yASRIlY5pZ73nclYJfNANGWPwFDZw==} + engines: {node: '>=10'} + hasBin: true + + test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + + text-decoding@1.0.0: + resolution: {integrity: sha512-/0TJD42KDnVwKmDK6jj3xP7E2MG7SHAOG4tyTgyUCRPdHwvkquYNLEQltmdMa3owq3TkddCVcTsoctJI8VQNKA==} + + text-table@0.2.0: + resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} + + thingies@1.21.0: + resolution: {integrity: sha512-hsqsJsFMsV+aD4s3CWKk85ep/3I9XzYV/IXaSouJMYIoDlgyi11cBhsqYe9/geRfB0YIikBQg6raRaM+nIMP9g==} + engines: {node: '>=10.18'} + peerDependencies: + tslib: ^2 + + thread-loader@3.0.4: + resolution: {integrity: sha512-ByaL2TPb+m6yArpqQUZvP+5S1mZtXsEP7nWKKlAUTm7fCml8kB5s1uI3+eHRP2bk5mVYfRSBI7FFf+tWEyLZwA==} + engines: {node: '>= 10.13.0'} + peerDependencies: + webpack: ^4.27.0 || ^5.0.0 + + through2@2.0.5: + resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} + + through@2.3.8: + resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==} + + time-fix-plugin@2.0.7: + resolution: {integrity: sha512-uVFet1LQToeUX0rTcSiYVYVoGuBpc8gP/2jnlUzuHMHe+gux6XLsNzxLUweabMwiUj5ejhoIMsUI55nVSEa/Vw==} + peerDependencies: + webpack: '>=4.0.0' + + timers-browserify@2.0.12: + resolution: {integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==} + engines: {node: '>=0.6.0'} + + tinyexec@1.0.2: + resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} + engines: {node: '>=18'} + + tldts-core@6.1.86: + resolution: {integrity: sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==} + + tldts@6.1.86: + resolution: {integrity: sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==} + hasBin: true + + tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + + tmpl@1.0.5: + resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==} + + to-arraybuffer@1.0.1: + resolution: {integrity: sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==} + + to-fast-properties@1.0.3: + resolution: {integrity: sha512-lxrWP8ejsq+7E3nNjwYmUBMAgjMTZoTI+sdBOpvNyijeDLa29LUn9QaoXAHv4+Z578hbmHHJKZknzxVtvo77og==} + engines: {node: '>=0.10.0'} + + to-object-path@0.3.0: + resolution: {integrity: sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==} + engines: {node: '>=0.10.0'} + + to-regex-range@2.1.1: + resolution: {integrity: sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==} + engines: {node: '>=0.10.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + to-regex@3.0.2: + resolution: {integrity: sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==} + engines: {node: '>=0.10.0'} + + toidentifier@1.0.1: + resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} + engines: {node: '>=0.6'} + + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + + tough-cookie@5.1.2: + resolution: {integrity: sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==} + engines: {node: '>=16'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tr46@5.1.1: + resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} + engines: {node: '>=18'} + + tree-dump@1.0.2: + resolution: {integrity: sha512-dpev9ABuLWdEubk+cIaI9cHwRNNDjkBBLXTwI4UCUFdQ5xXKqNXoK4FEciw/vxf+NQ7Cb7sGUyeUtORvHIdRXQ==} + engines: {node: '>=10.0'} + peerDependencies: + tslib: '2' + + trim-newlines@4.1.1: + resolution: {integrity: sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ==} + engines: {node: '>=12'} + + ts-api-utils@1.0.3: + resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==} + engines: {node: '>=16.13.0'} + peerDependencies: + typescript: '>=4.2.0' + + ts-jest@29.4.6: + resolution: {integrity: sha512-fSpWtOO/1AjSNQguk43hb/JCo16oJDnMJf3CdEGNkqsEX3t0KX96xvyX1D7PfLCpVoKu4MfVrqUkFyblYoY4lA==} + engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} + hasBin: true + peerDependencies: + '@babel/core': '>=7.0.0-beta.0 <8' + '@jest/transform': ^29.0.0 || ^30.0.0 + '@jest/types': ^29.0.0 || ^30.0.0 + babel-jest: ^29.0.0 || ^30.0.0 + esbuild: '*' + jest: ^29.0.0 || ^30.0.0 + jest-util: ^29.0.0 || ^30.0.0 + typescript: '>=4.3 <6' + peerDependenciesMeta: + '@babel/core': + optional: true + '@jest/transform': + optional: true + '@jest/types': + optional: true + babel-jest: + optional: true + esbuild: + optional: true + jest-util: + optional: true + + ts-loader@8.4.0: + resolution: {integrity: sha512-6nFY3IZ2//mrPc+ImY3hNWx1vCHyEhl6V+wLmL4CZcm6g1CqX7UKrkc6y0i4FwcfOhxyMPCfaEvh20f4r9GNpw==} + engines: {node: '>=10.0.0'} + peerDependencies: + typescript: '*' + webpack: '*' + + ts-pnp@1.2.0: + resolution: {integrity: sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==} + engines: {node: '>=6'} + peerDependencies: + typescript: '*' + peerDependenciesMeta: + typescript: + optional: true + + tsconfig-paths@3.14.2: + resolution: {integrity: sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==} + + tsconfig@7.0.0: + resolution: {integrity: sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==} + + tslib@1.14.1: + resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==} + + tslib@2.6.2: + resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tty-browserify@0.0.0: + resolution: {integrity: sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==} + + tweetnacl@1.0.3: + resolution: {integrity: sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + type-detect@4.0.8: + resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==} + engines: {node: '>=4'} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@0.6.0: + resolution: {integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==} + engines: {node: '>=8'} + + type-fest@0.8.1: + resolution: {integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==} + engines: {node: '>=8'} + + type-fest@1.4.0: + resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} + engines: {node: '>=10'} + + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} + engines: {node: '>=16'} + + typed-array-buffer@1.0.0: + resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.0: + resolution: {integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.0: + resolution: {integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.4: + resolution: {integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==} + + typedarray-to-buffer@3.1.5: + resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==} + + typedarray@0.0.6: + resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} + + typescript@4.9.5: + resolution: {integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==} + engines: {node: '>=4.2.0'} + hasBin: true + + ua-parser-js@1.0.38: + resolution: {integrity: sha512-Aq5ppTOfvrCMgAPneW1HfWj66Xi7XL+/mIy996R1/CLS/rcyJQm6QZdsKrUeivDFQ+Oc9Wyuwor8Ze8peEoUoQ==} + + ufo@1.6.1: + resolution: {integrity: sha512-9a4/uxlTWJ4+a5i0ooc1rU7C7YOw3wT+UGqdeNNHWnOF9qcMBgLRS+4IYUqbczewFx4mLEig6gawh7X6mFlEkA==} + + uglify-js@3.19.3: + resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} + engines: {node: '>=0.8.0'} + hasBin: true + + unbox-primitive@1.0.2: + resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==} + + uncrypto@0.1.3: + resolution: {integrity: sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q==} + + unctx@2.3.1: + resolution: {integrity: sha512-PhKke8ZYauiqh3FEMVNm7ljvzQiph0Mt3GBRve03IJm7ukfaON2OBK795tLwhbyfzknuRRkW0+Ze+CQUmzOZ+A==} + + undici-types@7.13.0: + resolution: {integrity: sha512-Ov2Rr9Sx+fRgagJ5AX0qvItZG/JKKoBRAVITs1zk7IqZGTJUwgUr7qoYBpWwakpWilTZFM98rG/AFRocu10iIQ==} + + undici-types@7.16.0: + resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} + + undici@6.19.7: + resolution: {integrity: sha512-HR3W/bMGPSr90i8AAp2C4DM3wChFdJPLrWYpIS++LxS8K+W535qftjt+4MyjNYHeWabMj1nvtmLIi7l++iq91A==} + engines: {node: '>=18.17'} + + unfetch@5.0.0: + resolution: {integrity: sha512-3xM2c89siXg0nHvlmYsQ2zkLASvVMBisZm5lF3gFDqfF2xonNStDJyMpvaOBe0a1Edxmqrf2E0HBdmy9QyZaeg==} + + unicode-canonical-property-names-ecmascript@2.0.0: + resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==} + engines: {node: '>=4'} + + unicode-match-property-ecmascript@2.0.0: + resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==} + engines: {node: '>=4'} + + unicode-match-property-value-ecmascript@2.1.0: + resolution: {integrity: sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==} + engines: {node: '>=4'} + + unicode-property-aliases-ecmascript@2.1.0: + resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==} + engines: {node: '>=4'} + + unicorn-magic@0.1.0: + resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} + engines: {node: '>=18'} + + unimport@3.4.0: + resolution: {integrity: sha512-M/lfFEgufIT156QAr/jWHLUn55kEmxBBiQsMxvRSIbquwmeJEyQYgshHDEvQDWlSJrVOOTAgnJ3FvlsrpGkanA==} + + unimport@3.7.2: + resolution: {integrity: sha512-91mxcZTadgXyj3lFWmrGT8GyoRHWuE5fqPOjg5RVtF6vj+OfM5G6WCzXjuYtSgELE5ggB34RY4oiCSEP8I3AHw==} + + union-value@1.0.1: + resolution: {integrity: sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==} + engines: {node: '>=0.10.0'} + + unique-filename@1.1.1: + resolution: {integrity: sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==} + + unique-slug@2.0.2: + resolution: {integrity: sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==} + + unique-string@2.0.0: + resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==} + engines: {node: '>=8'} + + universalify@0.1.2: + resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} + engines: {node: '>= 4.0.0'} + + universalify@2.0.0: + resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} + engines: {node: '>= 10.0.0'} + + unpipe@1.0.0: + resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} + engines: {node: '>= 0.8'} + + unplugin@1.11.0: + resolution: {integrity: sha512-3r7VWZ/webh0SGgJScpWl2/MRCZK5d3ZYFcNaeci/GQ7Teop7zf0Nl2pUuz7G21BwPd9pcUPOC5KmJ2L3WgC5g==} + engines: {node: '>=14.0.0'} + + unplugin@1.5.0: + resolution: {integrity: sha512-9ZdRwbh/4gcm1JTOkp9lAkIDrtOyOxgHmY7cjuwI8L/2RTikMcVG25GsZwNAgRuap3iDw2jeq7eoqtAsz5rW3A==} + + unrs-resolver@1.11.1: + resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} + + unset-value@1.0.0: + resolution: {integrity: sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==} + engines: {node: '>=0.10.0'} + + untyped@1.4.0: + resolution: {integrity: sha512-Egkr/s4zcMTEuulcIb7dgURS6QpN7DyqQYdf+jBtiaJvQ+eRsrtWUoX84SbvQWuLkXsOjM+8sJC9u6KoMK/U7Q==} + hasBin: true + + untyped@1.4.2: + resolution: {integrity: sha512-nC5q0DnPEPVURPhfPQLahhSTnemVtPzdx7ofiRxXpOB2SYnb3MfdU3DVGyJdS8Lx+tBWeAePO8BfU/3EgksM7Q==} + hasBin: true + + upath@1.2.0: + resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} + engines: {node: '>=4'} + + upath@2.0.1: + resolution: {integrity: sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==} + engines: {node: '>=4'} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + urix@0.1.0: + resolution: {integrity: sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==} + deprecated: Please see https://github.com/lydell/urix#deprecated + + url-loader@4.1.1: + resolution: {integrity: sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==} + engines: {node: '>= 10.13.0'} + peerDependencies: + file-loader: '*' + webpack: ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + file-loader: + optional: true + + url@0.11.3: + resolution: {integrity: sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==} + + use@3.1.1: + resolution: {integrity: sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==} + engines: {node: '>=0.10.0'} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + util.promisify@1.0.0: + resolution: {integrity: sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==} + + util@0.10.4: + resolution: {integrity: sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==} + + util@0.11.1: + resolution: {integrity: sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==} + + utila@0.4.0: + resolution: {integrity: sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==} + + utils-merge@1.0.1: + resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} + engines: {node: '>= 0.4.0'} + + uuid@8.3.2: + resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} + hasBin: true + + v8-to-istanbul@9.3.0: + resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} + engines: {node: '>=10.12.0'} + + validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + + vary@1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + + vite-plugin-eslint@1.8.1: + resolution: {integrity: sha512-PqdMf3Y2fLO9FsNPmMX+//2BF5SF8nEWspZdgl4kSt7UvHDRHVVfHvxsD7ULYzZrJDGRxR81Nq7TOFgwMnUang==} + peerDependencies: + eslint: '>=7' + vite: '>=2' + + vite-plugin-stylelint@5.3.1: + resolution: {integrity: sha512-M/hSdfOwnOVghbJDeuuYIU2xO/MMukYR8QcEyNKFPG8ro1L+DlTdViix2B2d/FvAw14WPX88ckA5A7NvUjJz8w==} + engines: {node: '>=14.18'} + peerDependencies: + '@types/stylelint': ^13.0.0 + postcss: ^7.0.0 || ^8.0.0 + rollup: ^2.0.0 || ^3.0.0 || ^4.0.0 + stylelint: ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + vite: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + '@types/stylelint': + optional: true + postcss: + optional: true + rollup: + optional: true + + vite@4.5.3: + resolution: {integrity: sha512-kQL23kMeX92v3ph7IauVkXkikdDRsYMGTVl5KY2E9OY4ONLvkHf04MDTbnfo6NKxZiDLWzVpP5oTa8hQD8U3dg==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + + vm-browserify@1.1.2: + resolution: {integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==} + + vue-chartjs@5.3.3: + resolution: {integrity: sha512-jqxtL8KZ6YJ5NTv6XzrzLS7osyegOi28UGNZW0h9OkDL7Sh1396ht4Dorh04aKrl2LiSalQ84WtqiG0RIJb0tA==} + peerDependencies: + chart.js: ^4.1.1 + vue: ^3.0.0-0 || ^2.7.0 + + vue-class-component@7.2.6: + resolution: {integrity: sha512-+eaQXVrAm/LldalI272PpDe3+i4mPis0ORiMYxF6Ae4hyuCh15W8Idet7wPUEs4N4YptgFHGys4UrgNQOMyO6w==} + peerDependencies: + vue: ^2.0.0 + + vue-client-only@2.1.0: + resolution: {integrity: sha512-vKl1skEKn8EK9f8P2ZzhRnuaRHLHrlt1sbRmazlvsx6EiC3A8oWF8YCBrMJzoN+W3OnElwIGbVjsx6/xelY1AA==} + + vue-eslint-parser@9.3.1: + resolution: {integrity: sha512-Clr85iD2XFZ3lJ52/ppmUDG/spxQu6+MAeHXjjyI4I1NUYZ9xmenQp4N0oaHJhrA8OOxltCVxMRfANGa70vU0g==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '>=6.0.0' + + vue-eslint-parser@9.4.3: + resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==} + engines: {node: ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: '>=6.0.0' + + vue-glow@1.4.2: + resolution: {integrity: sha512-MDC5Q817fH51OhCpYopAcXwMZ49yVAjEgiJ1sXlc3Kyul0AU343AbB0zflr+LnuiuS/EegfVkxYh0I67xSMYZw==} + + vue-hot-reload-api@2.3.4: + resolution: {integrity: sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==} + + vue-jest@3.0.7: + resolution: {integrity: sha512-PIOxFM+wsBMry26ZpfBvUQ/DGH2hvp5khDQ1n51g3bN0TwFwTy4J85XVfxTRMukqHji/GnAoGUnlZ5Ao73K62w==} + peerDependencies: + babel-core: ^6.25.0 || ^7.0.0-0 + vue: ^2.x + vue-template-compiler: ^2.x + + vue-loader@15.11.1: + resolution: {integrity: sha512-0iw4VchYLePqJfJu9s62ACWUXeSqM30SQqlIftbYWM3C+jpPcEHKSPUZBLjSF9au4HTHQ/naF6OGnO3Q/qGR3Q==} + peerDependencies: + '@vue/compiler-sfc': ^3.0.8 + cache-loader: '*' + css-loader: '*' + prettier: '*' + vue-template-compiler: '*' + webpack: ^3.0.0 || ^4.1.0 || ^5.0.0-0 + peerDependenciesMeta: + '@vue/compiler-sfc': + optional: true + cache-loader: + optional: true + prettier: + optional: true + vue-template-compiler: + optional: true + + vue-meta@2.4.0: + resolution: {integrity: sha512-XEeZUmlVeODclAjCNpWDnjgw+t3WA6gdzs6ENoIAgwO1J1d5p1tezDhtteLUFwcaQaTtayRrsx7GL6oXp/m2Jw==} + + vue-no-ssr@1.1.1: + resolution: {integrity: sha512-ZMjqRpWabMPqPc7gIrG0Nw6vRf1+itwf0Itft7LbMXs2g3Zs/NFmevjZGN1x7K3Q95GmIjWbQZTVerxiBxI+0g==} + + vue-property-decorator@9.1.2: + resolution: {integrity: sha512-xYA8MkZynPBGd/w5QFJ2d/NM0z/YeegMqYTphy7NJQXbZcuU6FC6AOdUAcy4SXP+YnkerC6AfH+ldg7PDk9ESQ==} + peerDependencies: + vue: '*' + vue-class-component: '*' + + vue-router@3.6.5: + resolution: {integrity: sha512-VYXZQLtjuvKxxcshuRAwjHnciqZVoXAjTjcqBTz4rKc8qih9g9pI3hbDjmqXaHdgL3v8pV6P8Z335XvHzESxLQ==} + peerDependencies: + vue: ^2 + + vue-server-renderer@2.7.16: + resolution: {integrity: sha512-U7GgR4rYmHmbs3Z2gqsasfk7JNuTsy/xrR5EMMGRLkjN8+ryDlqQq6Uu3DcmbCATAei814YOxyl0eq2HNqgXyQ==} + + vue-style-loader@4.1.3: + resolution: {integrity: sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==} + + vue-template-compiler@2.7.16: + resolution: {integrity: sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==} + + vue-template-es2015-compiler@1.9.1: + resolution: {integrity: sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==} + + vue@2.7.16: + resolution: {integrity: sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==} + deprecated: Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details. + + vuetify-loader@1.9.2: + resolution: {integrity: sha512-8PP2w7aAs/rjA+Izec6qY7sHVb75MNrGQrDOTZJ5IEnvl+NiFhVpU2iWdRDZ3eMS842cWxSWStvkr+KJJKy+Iw==} + peerDependencies: + gm: ^1.23.0 + pug: ^2.0.0 || ^3.0.0 + sharp: '*' + vue: ^2.7.2 + vuetify: ^1.3.0 || ^2.0.0 + webpack: ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + gm: + optional: true + pug: + optional: true + sharp: + optional: true + + vuetify@2.7.2: + resolution: {integrity: sha512-qr04ww7uzAPQbpk751x4fSdjsJ+zREzjQ/rBlcQGuWS6MIMFMXcXcwvp4+/tnGsULZxPMWfQ0kmZmg5Yc/XzgQ==} + deprecated: This version is deprecated + peerDependencies: + vue: ^2.6.4 + + vuex@3.6.2: + resolution: {integrity: sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==} + peerDependencies: + vue: ^2.0.0 + + w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + + walker@1.0.8: + resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==} + + watchpack-chokidar2@2.0.1: + resolution: {integrity: sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==} + + watchpack@1.7.5: + resolution: {integrity: sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==} + + watchpack@2.5.0: + resolution: {integrity: sha512-e6vZvY6xboSwLz2GD36c16+O/2Z6fKvIf4pOXptw2rY9MVwE/TXc6RGqxD3I3x0a28lwBY7DE+76uTPSsBrrCA==} + engines: {node: '>=10.13.0'} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + webpack-bundle-analyzer@4.10.2: + resolution: {integrity: sha512-vJptkMm9pk5si4Bv922ZbKLV8UTT4zib4FPgXMhgzUny0bfDDkLXAVQs3ly3fS4/TN9ROFtb0NFrm04UXFE/Vw==} + engines: {node: '>= 10.13.0'} + hasBin: true + + webpack-dev-middleware@5.3.4: + resolution: {integrity: sha512-BVdTqhhs+0IfoeAf7EoH5WE+exCmqGerHfDM0IL096Px60Tq2Mn9MAbnaGUe6HiMa41KMCYF19gyzZmBcq/o4Q==} + engines: {node: '>= 12.13.0'} + peerDependencies: + webpack: ^4.0.0 || ^5.0.0 + + webpack-hot-middleware@2.26.1: + resolution: {integrity: sha512-khZGfAeJx6I8K9zKohEWWYN6KDlVw2DHownoe+6Vtwj1LP9WFgegXnVMSkZ/dBEBtXFwrkkydsaPFlB7f8wU2A==} + + webpack-node-externals@3.0.0: + resolution: {integrity: sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==} + engines: {node: '>=6'} + + webpack-sources@1.4.3: + resolution: {integrity: sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==} + + webpack-sources@3.3.3: + resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} + engines: {node: '>=10.13.0'} + + webpack-virtual-modules@0.5.0: + resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==} + + webpack-virtual-modules@0.6.2: + resolution: {integrity: sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==} + + webpack@4.47.0: + resolution: {integrity: sha512-td7fYwgLSrky3fI1EuU5cneU4+pbH6GgOfuKNS1tNPcfdGinGELAqsb/BP4nnvZyKSG2i/xFGU7+n2PvZA8HJQ==} + engines: {node: '>=6.11.5'} + hasBin: true + peerDependencies: + webpack-cli: '*' + webpack-command: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + webpack-command: + optional: true + + webpack@5.104.1: + resolution: {integrity: sha512-Qphch25abbMNtekmEGJmeRUhLDbe+QfiWTiqpKYkpCOWY64v9eyl+KRRLmqOFA2AvKPpc9DC6+u2n76tQLBoaA==} + engines: {node: '>=10.13.0'} + hasBin: true + peerDependencies: + webpack-cli: '*' + peerDependenciesMeta: + webpack-cli: + optional: true + + webpackbar@6.0.1: + resolution: {integrity: sha512-TnErZpmuKdwWBdMoexjio3KKX6ZtoKHRVvLIU0A47R0VVBDtx3ZyOJDktgYixhoJokZTYTt1Z37OkO9pnGJa9Q==} + engines: {node: '>=14.21.3'} + peerDependencies: + webpack: 3 || 4 || 5 + + websocket-driver@0.7.4: + resolution: {integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==} + engines: {node: '>=0.8.0'} + + websocket-extensions@0.1.4: + resolution: {integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==} + engines: {node: '>=0.8.0'} + + whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation + + whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + + whatwg-url@14.2.0: + resolution: {integrity: sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==} + engines: {node: '>=18'} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + which-boxed-primitive@1.0.2: + resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==} + + which-module@2.0.1: + resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==} + + which-typed-array@1.1.11: + resolution: {integrity: sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==} + engines: {node: '>= 0.4'} + + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + widest-line@3.1.0: + resolution: {integrity: sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==} + engines: {node: '>=8'} + + wordwrap@1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + + worker-farm@1.7.0: + resolution: {integrity: sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==} + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrap-ansi@9.0.0: + resolution: {integrity: sha512-G8ura3S+3Z2G+mkgNRq8dqaFZAuxfsxpBB8OCTGRTCtp+l/v9nbFNmCUP1BZMts3G1142MsZfn6eeUKrr4PD1Q==} + engines: {node: '>=18'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + write-file-atomic@2.4.3: + resolution: {integrity: sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==} + + write-file-atomic@3.0.3: + resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==} + + write-file-atomic@5.0.1: + resolution: {integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + write-json-file@2.3.0: + resolution: {integrity: sha512-84+F0igFp2dPD6UpAQjOUX3CdKUOqUzn6oE9sDBNzUXINR5VceJ1rauZltqQB/bcYsx3EpKys4C7/PivKUAiWQ==} + engines: {node: '>=4'} + + ws@7.5.10: + resolution: {integrity: sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==} + engines: {node: '>=8.3.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ^5.0.2 + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + + xdg-basedir@4.0.0: + resolution: {integrity: sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==} + engines: {node: '>=8'} + + xml-name-validator@4.0.0: + resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} + engines: {node: '>=12'} + + xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + + xmlbuilder@13.0.2: + resolution: {integrity: sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ==} + engines: {node: '>=6.0'} + + xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + + xtend@4.0.2: + resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} + engines: {node: '>=0.4'} + + xxhashjs@0.2.2: + resolution: {integrity: sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==} + + y18n@4.0.3: + resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yallist@2.1.2: + resolution: {integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + + yaml@2.8.1: + resolution: {integrity: sha512-lcYcMxX2PO9XMGvAJkJ3OsNMw+/7FKes7/hgerGUYWIoWu5j/+YQqcZr5JnPZWzOsEBgMbSbiSTn/dv/69Mkpw==} + engines: {node: '>= 14.6'} + hasBin: true + + yargs-parser@18.1.3: + resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==} + engines: {node: '>=6'} + + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs@15.4.1: + resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==} + engines: {node: '>=8'} + + yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + +snapshots: + + '@aashutoshrathi/word-wrap@1.2.6': {} + + '@ampproject/remapping@2.2.1': + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.31 + + '@asamuzakjp/css-color@3.2.0': + dependencies: + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + lru-cache: 10.4.3 + + '@babel/code-frame@7.22.13': + dependencies: + '@babel/highlight': 7.23.4 + chalk: 2.4.2 + + '@babel/code-frame@7.23.5': + dependencies: + '@babel/highlight': 7.23.4 + chalk: 2.4.2 + + '@babel/code-frame@7.24.7': + dependencies: + '@babel/highlight': 7.24.7 + picocolors: 1.1.1 + + '@babel/code-frame@7.27.1': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.24.7': {} + + '@babel/compat-data@7.28.4': {} + + '@babel/core@7.24.7': + dependencies: + '@ampproject/remapping': 2.2.1 + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.24.7 + '@babel/helper-compilation-targets': 7.24.7 + '@babel/helper-module-transforms': 7.24.7(@babel/core@7.24.7) + '@babel/helpers': 7.24.7 + '@babel/parser': 7.28.0 + '@babel/template': 7.24.7 + '@babel/traverse': 7.24.7 + '@babel/types': 7.28.2 + convert-source-map: 2.0.0 + debug: 4.4.1 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/core@7.28.4': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4) + '@babel/helpers': 7.28.4 + '@babel/parser': 7.28.4 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/eslint-parser@7.28.6(@babel/core@7.28.4)(eslint@8.57.1)': + dependencies: + '@babel/core': 7.28.4 + '@nicolo-ribaudo/eslint-scope-5-internals': 5.1.1-v1 + eslint: 8.57.1 + eslint-visitor-keys: 2.1.0 + semver: 6.3.1 + + '@babel/generator@7.24.7': + dependencies: + '@babel/types': 7.28.4 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 2.5.2 + + '@babel/generator@7.28.3': + dependencies: + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-annotate-as-pure@7.22.5': + dependencies: + '@babel/types': 7.28.4 + + '@babel/helper-annotate-as-pure@7.24.7': + dependencies: + '@babel/types': 7.28.4 + + '@babel/helper-builder-binary-assignment-operator-visitor@7.24.7': + dependencies: + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-compilation-targets@7.24.7': + dependencies: + '@babel/compat-data': 7.24.7 + '@babel/helper-validator-option': 7.24.7 + browserslist: 4.28.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-compilation-targets@7.27.2': + dependencies: + '@babel/compat-data': 7.28.4 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.1 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.24.5(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@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/core@7.24.7) + '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 + '@babel/helper-split-export-declaration': 7.24.5 + semver: 6.3.1 + + '@babel/helper-create-class-features-plugin@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-annotate-as-pure': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-function-name': 7.24.7 + '@babel/helper-member-expression-to-functions': 7.24.7 + '@babel/helper-optimise-call-expression': 7.24.7 + '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.7) + '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 + '@babel/helper-split-export-declaration': 7.24.7 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-create-regexp-features-plugin@7.22.15(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-annotate-as-pure': 7.22.5 + regexpu-core: 5.3.2 + semver: 6.3.1 + + '@babel/helper-create-regexp-features-plugin@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-annotate-as-pure': 7.24.7 + regexpu-core: 5.3.2 + semver: 6.3.1 + + '@babel/helper-define-polyfill-provider@0.6.2(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + debug: 4.4.3 + lodash.debounce: 4.0.8 + resolve: 1.22.6 + transitivePeerDependencies: + - supports-color + + '@babel/helper-environment-visitor@7.22.20': {} + + '@babel/helper-environment-visitor@7.24.7': + dependencies: + '@babel/types': 7.28.4 + + '@babel/helper-function-name@7.23.0': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.4 + + '@babel/helper-function-name@7.24.7': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.4 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-hoist-variables@7.24.7': + dependencies: + '@babel/types': 7.28.4 + + '@babel/helper-member-expression-to-functions@7.24.5': + dependencies: + '@babel/types': 7.28.4 + + '@babel/helper-member-expression-to-functions@7.24.7': + dependencies: + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.24.7': + dependencies: + '@babel/traverse': 7.24.7 + '@babel/types': 7.28.2 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-imports@7.27.1': + dependencies: + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-simple-access': 7.24.7 + '@babel/helper-split-export-declaration': 7.24.7 + '@babel/helper-validator-identifier': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-module-imports': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + '@babel/traverse': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-optimise-call-expression@7.22.5': + dependencies: + '@babel/types': 7.28.4 + + '@babel/helper-optimise-call-expression@7.24.7': + dependencies: + '@babel/types': 7.28.4 + + '@babel/helper-plugin-utils@7.27.1': {} + + '@babel/helper-remap-async-to-generator@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-annotate-as-pure': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-wrap-function': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-replace-supers@7.24.1(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-environment-visitor': 7.22.20 + '@babel/helper-member-expression-to-functions': 7.24.5 + '@babel/helper-optimise-call-expression': 7.22.5 + + '@babel/helper-replace-supers@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-member-expression-to-functions': 7.24.7 + '@babel/helper-optimise-call-expression': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-simple-access@7.24.7': + dependencies: + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-skip-transparent-expression-wrappers@7.22.5': + dependencies: + '@babel/types': 7.28.4 + + '@babel/helper-skip-transparent-expression-wrappers@7.24.7': + dependencies: + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-split-export-declaration@7.24.5': + dependencies: + '@babel/types': 7.28.4 + + '@babel/helper-split-export-declaration@7.24.7': + dependencies: + '@babel/types': 7.28.4 + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.24.5': {} + + '@babel/helper-validator-identifier@7.27.1': {} + + '@babel/helper-validator-option@7.24.7': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helper-wrap-function@7.24.7': + dependencies: + '@babel/helper-function-name': 7.24.7 + '@babel/template': 7.27.2 + '@babel/traverse': 7.28.4 + '@babel/types': 7.28.4 + transitivePeerDependencies: + - supports-color + + '@babel/helpers@7.24.7': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.4 + + '@babel/helpers@7.28.4': + dependencies: + '@babel/template': 7.27.2 + '@babel/types': 7.28.4 + + '@babel/highlight@7.23.4': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + chalk: 2.4.2 + js-tokens: 4.0.0 + + '@babel/highlight@7.24.7': + dependencies: + '@babel/helper-validator-identifier': 7.27.1 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/parser@7.24.0': + dependencies: + '@babel/types': 7.28.2 + + '@babel/parser@7.28.0': + dependencies: + '@babel/types': 7.28.2 + + '@babel/parser@7.28.4': + dependencies: + '@babel/types': 7.28.4 + + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 + '@babel/plugin-transform-optional-chaining': 7.24.7(@babel/core@7.24.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-create-class-features-plugin': 7.24.5(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-proposal-decorators@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-decorators': 7.24.7(@babel/core@7.24.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7) + + '@babel/plugin-proposal-optional-chaining@7.21.0(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7) + + '@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-create-class-features-plugin': 7.24.5(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + + '@babel/plugin-proposal-private-property-in-object@7.21.11(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-annotate-as-pure': 7.22.5 + '@babel/helper-create-class-features-plugin': 7.24.5(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.7) + + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-decorators@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-assertions@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-attributes@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.4)': + dependencies: + '@babel/core': 7.28.4 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-arrow-functions@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-async-generator-functions@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-async-to-generator@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-remap-async-to-generator': 7.24.7(@babel/core@7.24.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-block-scoped-functions@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-block-scoping@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-class-properties@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-class-static-block@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-classes@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-annotate-as-pure': 7.24.7 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-function-name': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.7) + '@babel/helper-split-export-declaration': 7.24.7 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-computed-properties@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/template': 7.27.2 + + '@babel/plugin-transform-destructuring@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-dotall-regex@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-duplicate-keys@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-dynamic-import@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.7) + + '@babel/plugin-transform-exponentiation-operator@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-builder-binary-assignment-operator-visitor': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-export-namespace-from@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.7) + + '@babel/plugin-transform-for-of@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-function-name@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-function-name': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-json-strings@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.7) + + '@babel/plugin-transform-literals@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-logical-assignment-operators@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.7) + + '@babel/plugin-transform-member-expression-literals@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-modules-amd@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-commonjs@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-simple-access': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-systemjs@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-hoist-variables': 7.24.7 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-modules-umd@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-module-transforms': 7.28.3(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-named-capturing-groups-regex@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-new-target@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-nullish-coalescing-operator@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7) + + '@babel/plugin-transform-numeric-separator@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.7) + + '@babel/plugin-transform-object-rest-spread@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-compilation-targets': 7.27.2 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.7) + + '@babel/plugin-transform-object-super@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-replace-supers': 7.24.7(@babel/core@7.24.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-optional-catch-binding@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.7) + + '@babel/plugin-transform-optional-chaining@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-parameters@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-private-methods@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.27.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-private-property-in-object@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-annotate-as-pure': 7.24.7 + '@babel/helper-create-class-features-plugin': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.27.1 + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-property-literals@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-regenerator@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + regenerator-transform: 0.15.2 + + '@babel/plugin-transform-reserved-words@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-runtime@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.24.7) + babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.24.7) + babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.24.7) + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-shorthand-properties@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-spread@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-skip-transparent-expression-wrappers': 7.24.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-sticky-regex@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-template-literals@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-typeof-symbol@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-escapes@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-property-regex@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-regex@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/plugin-transform-unicode-sets-regex@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-create-regexp-features-plugin': 7.24.7(@babel/core@7.24.7) + '@babel/helper-plugin-utils': 7.27.1 + + '@babel/preset-env@7.24.7(@babel/core@7.24.7)': + dependencies: + '@babel/compat-data': 7.24.7 + '@babel/core': 7.24.7 + '@babel/helper-compilation-targets': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-validator-option': 7.24.7 + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.24.7) + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.7) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.7) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.24.7) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-import-assertions': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-syntax-import-attributes': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.7) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.7) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.7) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.7) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.24.7) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.7) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.24.7) + '@babel/plugin-transform-arrow-functions': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-async-generator-functions': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-async-to-generator': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-block-scoped-functions': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-block-scoping': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-class-properties': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-class-static-block': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-classes': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-computed-properties': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-destructuring': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-dotall-regex': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-duplicate-keys': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-dynamic-import': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-exponentiation-operator': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-export-namespace-from': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-for-of': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-function-name': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-json-strings': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-literals': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-logical-assignment-operators': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-member-expression-literals': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-modules-amd': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-modules-commonjs': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-modules-systemjs': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-modules-umd': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-named-capturing-groups-regex': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-new-target': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-nullish-coalescing-operator': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-numeric-separator': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-object-rest-spread': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-object-super': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-optional-catch-binding': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-optional-chaining': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-parameters': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-private-methods': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-private-property-in-object': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-property-literals': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-regenerator': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-reserved-words': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-shorthand-properties': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-spread': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-sticky-regex': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-template-literals': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-typeof-symbol': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-unicode-escapes': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-unicode-property-regex': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-unicode-regex': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-transform-unicode-sets-regex': 7.24.7(@babel/core@7.24.7) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.24.7) + babel-plugin-polyfill-corejs2: 0.4.11(@babel/core@7.24.7) + babel-plugin-polyfill-corejs3: 0.10.4(@babel/core@7.24.7) + babel-plugin-polyfill-regenerator: 0.6.2(@babel/core@7.24.7) + core-js-compat: 3.37.1 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.24.7)': + dependencies: + '@babel/core': 7.24.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/types': 7.28.4 + esutils: 2.0.3 + + '@babel/regjsgen@0.8.0': {} + + '@babel/runtime@7.24.5': + dependencies: + regenerator-runtime: 0.14.1 + + '@babel/runtime@7.24.7': + dependencies: + regenerator-runtime: 0.14.1 + + '@babel/standalone@7.23.1': {} + + '@babel/standalone@7.24.7': {} + + '@babel/template@7.24.7': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + + '@babel/template@7.27.2': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + + '@babel/traverse@7.24.7': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-environment-visitor': 7.24.7 + '@babel/helper-function-name': 7.24.7 + '@babel/helper-hoist-variables': 7.24.7 + '@babel/helper-split-export-declaration': 7.24.7 + '@babel/parser': 7.28.4 + '@babel/types': 7.28.4 + debug: 4.4.3 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/traverse@7.28.4': + dependencies: + '@babel/code-frame': 7.27.1 + '@babel/generator': 7.28.3 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.28.4 + '@babel/template': 7.27.2 + '@babel/types': 7.28.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.28.2': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@babel/types@7.28.4': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.27.1 + + '@bcoe/v8-coverage@0.2.3': {} + + '@commitlint/cli@20.4.0(@types/node@25.1.0)(typescript@4.9.5)': + dependencies: + '@commitlint/format': 20.4.0 + '@commitlint/lint': 20.4.0 + '@commitlint/load': 20.4.0(@types/node@25.1.0)(typescript@4.9.5) + '@commitlint/read': 20.4.0 + '@commitlint/types': 20.4.0 + tinyexec: 1.0.2 + yargs: 17.7.2 + transitivePeerDependencies: + - '@types/node' + - typescript + + '@commitlint/config-conventional@20.4.0': + dependencies: + '@commitlint/types': 20.4.0 + conventional-changelog-conventionalcommits: 9.1.0 + + '@commitlint/config-validator@20.4.0': + dependencies: + '@commitlint/types': 20.4.0 + ajv: 8.17.1 + + '@commitlint/ensure@20.4.0': + dependencies: + '@commitlint/types': 20.4.0 + kasi: 2.0.1 + + '@commitlint/execute-rule@20.0.0': {} + + '@commitlint/format@20.4.0': + dependencies: + '@commitlint/types': 20.4.0 + picocolors: 1.1.1 + + '@commitlint/is-ignored@20.4.0': + dependencies: + '@commitlint/types': 20.4.0 + semver: 7.7.3 + + '@commitlint/lint@20.4.0': + dependencies: + '@commitlint/is-ignored': 20.4.0 + '@commitlint/parse': 20.4.0 + '@commitlint/rules': 20.4.0 + '@commitlint/types': 20.4.0 + + '@commitlint/load@20.4.0(@types/node@25.1.0)(typescript@4.9.5)': + dependencies: + '@commitlint/config-validator': 20.4.0 + '@commitlint/execute-rule': 20.0.0 + '@commitlint/resolve-extends': 20.4.0 + '@commitlint/types': 20.4.0 + cosmiconfig: 9.0.0(typescript@4.9.5) + cosmiconfig-typescript-loader: 6.2.0(@types/node@25.1.0)(cosmiconfig@9.0.0(typescript@4.9.5))(typescript@4.9.5) + is-plain-obj: 4.1.0 + lodash.mergewith: 4.6.2 + picocolors: 1.1.1 + transitivePeerDependencies: + - '@types/node' + - typescript + + '@commitlint/message@20.4.0': {} + + '@commitlint/parse@20.4.0': + dependencies: + '@commitlint/types': 20.4.0 + conventional-changelog-angular: 8.1.0 + conventional-commits-parser: 6.2.1 + + '@commitlint/read@20.4.0': + dependencies: + '@commitlint/top-level': 20.4.0 + '@commitlint/types': 20.4.0 + git-raw-commits: 4.0.0 + minimist: 1.2.8 + tinyexec: 1.0.2 + + '@commitlint/resolve-extends@20.4.0': + dependencies: + '@commitlint/config-validator': 20.4.0 + '@commitlint/types': 20.4.0 + global-directory: 4.0.1 + import-meta-resolve: 4.2.0 + lodash.mergewith: 4.6.2 + resolve-from: 5.0.0 + + '@commitlint/rules@20.4.0': + dependencies: + '@commitlint/ensure': 20.4.0 + '@commitlint/message': 20.4.0 + '@commitlint/to-lines': 20.0.0 + '@commitlint/types': 20.4.0 + + '@commitlint/to-lines@20.0.0': {} + + '@commitlint/top-level@20.4.0': + dependencies: + escalade: 3.2.0 + + '@commitlint/types@20.4.0': + dependencies: + conventional-commits-parser: 6.2.1 + picocolors: 1.1.1 + + '@csstools/cascade-layer-name-parser@1.0.12(@csstools/css-parser-algorithms@2.7.0(@csstools/css-tokenizer@2.3.2))(@csstools/css-tokenizer@2.3.2)': + dependencies: + '@csstools/css-parser-algorithms': 2.7.0(@csstools/css-tokenizer@2.3.2) + '@csstools/css-tokenizer': 2.3.2 + + '@csstools/color-helpers@4.2.1': {} + + '@csstools/color-helpers@5.1.0': {} + + '@csstools/css-calc@1.2.3(@csstools/css-parser-algorithms@2.7.0(@csstools/css-tokenizer@2.3.2))(@csstools/css-tokenizer@2.3.2)': + dependencies: + '@csstools/css-parser-algorithms': 2.7.0(@csstools/css-tokenizer@2.3.2) + '@csstools/css-tokenizer': 2.3.2 + + '@csstools/css-calc@1.2.4(@csstools/css-parser-algorithms@2.7.0(@csstools/css-tokenizer@2.3.2))(@csstools/css-tokenizer@2.3.2)': + dependencies: + '@csstools/css-parser-algorithms': 2.7.0(@csstools/css-tokenizer@2.3.2) + '@csstools/css-tokenizer': 2.3.2 + + '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-color-parser@2.0.3(@csstools/css-parser-algorithms@2.7.0(@csstools/css-tokenizer@2.3.2))(@csstools/css-tokenizer@2.3.2)': + dependencies: + '@csstools/color-helpers': 4.2.1 + '@csstools/css-calc': 1.2.4(@csstools/css-parser-algorithms@2.7.0(@csstools/css-tokenizer@2.3.2))(@csstools/css-tokenizer@2.3.2) + '@csstools/css-parser-algorithms': 2.7.0(@csstools/css-tokenizer@2.3.2) + '@csstools/css-tokenizer': 2.3.2 + + '@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/color-helpers': 5.1.0 + '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4) + '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4) + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-parser-algorithms@2.3.2(@csstools/css-tokenizer@2.2.1)': + dependencies: + '@csstools/css-tokenizer': 2.2.1 + + '@csstools/css-parser-algorithms@2.7.0(@csstools/css-tokenizer@2.3.2)': + dependencies: + '@csstools/css-tokenizer': 2.3.2 + + '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)': + dependencies: + '@csstools/css-tokenizer': 3.0.4 + + '@csstools/css-tokenizer@2.2.1': {} + + '@csstools/css-tokenizer@2.3.2': {} + + '@csstools/css-tokenizer@3.0.4': {} + + '@csstools/media-query-list-parser@2.1.12(@csstools/css-parser-algorithms@2.7.0(@csstools/css-tokenizer@2.3.2))(@csstools/css-tokenizer@2.3.2)': + dependencies: + '@csstools/css-parser-algorithms': 2.7.0(@csstools/css-tokenizer@2.3.2) + '@csstools/css-tokenizer': 2.3.2 + + '@csstools/media-query-list-parser@2.1.5(@csstools/css-parser-algorithms@2.3.2(@csstools/css-tokenizer@2.2.1))(@csstools/css-tokenizer@2.2.1)': + dependencies: + '@csstools/css-parser-algorithms': 2.3.2(@csstools/css-tokenizer@2.2.1) + '@csstools/css-tokenizer': 2.2.1 + + '@csstools/postcss-cascade-layers@4.0.6(postcss@8.5.6)': + dependencies: + '@csstools/selector-specificity': 3.1.1(postcss-selector-parser@6.1.2) + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + '@csstools/postcss-color-function@3.0.17(postcss@8.5.6)': + dependencies: + '@csstools/css-color-parser': 2.0.3(@csstools/css-parser-algorithms@2.7.0(@csstools/css-tokenizer@2.3.2))(@csstools/css-tokenizer@2.3.2) + '@csstools/css-parser-algorithms': 2.7.0(@csstools/css-tokenizer@2.3.2) + '@csstools/css-tokenizer': 2.3.2 + '@csstools/postcss-progressive-custom-properties': 3.2.0(postcss@8.5.6) + '@csstools/utilities': 1.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-color-mix-function@2.0.17(postcss@8.5.6)': + dependencies: + '@csstools/css-color-parser': 2.0.3(@csstools/css-parser-algorithms@2.7.0(@csstools/css-tokenizer@2.3.2))(@csstools/css-tokenizer@2.3.2) + '@csstools/css-parser-algorithms': 2.7.0(@csstools/css-tokenizer@2.3.2) + '@csstools/css-tokenizer': 2.3.2 + '@csstools/postcss-progressive-custom-properties': 3.2.0(postcss@8.5.6) + '@csstools/utilities': 1.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-exponential-functions@1.0.8(postcss@8.5.6)': + dependencies: + '@csstools/css-calc': 1.2.3(@csstools/css-parser-algorithms@2.7.0(@csstools/css-tokenizer@2.3.2))(@csstools/css-tokenizer@2.3.2) + '@csstools/css-parser-algorithms': 2.7.0(@csstools/css-tokenizer@2.3.2) + '@csstools/css-tokenizer': 2.3.2 + postcss: 8.5.6 + + '@csstools/postcss-font-format-keywords@3.0.2(postcss@8.5.6)': + dependencies: + '@csstools/utilities': 1.0.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-gamut-mapping@1.0.10(postcss@8.5.6)': + dependencies: + '@csstools/css-color-parser': 2.0.3(@csstools/css-parser-algorithms@2.7.0(@csstools/css-tokenizer@2.3.2))(@csstools/css-tokenizer@2.3.2) + '@csstools/css-parser-algorithms': 2.7.0(@csstools/css-tokenizer@2.3.2) + '@csstools/css-tokenizer': 2.3.2 + postcss: 8.5.6 + + '@csstools/postcss-gradients-interpolation-method@4.0.18(postcss@8.5.6)': + dependencies: + '@csstools/css-color-parser': 2.0.3(@csstools/css-parser-algorithms@2.7.0(@csstools/css-tokenizer@2.3.2))(@csstools/css-tokenizer@2.3.2) + '@csstools/css-parser-algorithms': 2.7.0(@csstools/css-tokenizer@2.3.2) + '@csstools/css-tokenizer': 2.3.2 + '@csstools/postcss-progressive-custom-properties': 3.2.0(postcss@8.5.6) + '@csstools/utilities': 1.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-hwb-function@3.0.16(postcss@8.5.6)': + dependencies: + '@csstools/css-color-parser': 2.0.3(@csstools/css-parser-algorithms@2.7.0(@csstools/css-tokenizer@2.3.2))(@csstools/css-tokenizer@2.3.2) + '@csstools/css-parser-algorithms': 2.7.0(@csstools/css-tokenizer@2.3.2) + '@csstools/css-tokenizer': 2.3.2 + '@csstools/postcss-progressive-custom-properties': 3.2.0(postcss@8.5.6) + '@csstools/utilities': 1.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-ic-unit@3.0.6(postcss@8.5.6)': + dependencies: + '@csstools/postcss-progressive-custom-properties': 3.2.0(postcss@8.5.6) + '@csstools/utilities': 1.0.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-initial@1.0.1(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + + '@csstools/postcss-is-pseudo-class@4.0.8(postcss@8.5.6)': + dependencies: + '@csstools/selector-specificity': 3.1.1(postcss-selector-parser@6.1.2) + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + '@csstools/postcss-light-dark-function@1.0.6(postcss@8.5.6)': + dependencies: + '@csstools/css-parser-algorithms': 2.7.0(@csstools/css-tokenizer@2.3.2) + '@csstools/css-tokenizer': 2.3.2 + '@csstools/postcss-progressive-custom-properties': 3.2.0(postcss@8.5.6) + '@csstools/utilities': 1.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-logical-float-and-clear@2.0.1(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + + '@csstools/postcss-logical-overflow@1.0.1(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + + '@csstools/postcss-logical-overscroll-behavior@1.0.1(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + + '@csstools/postcss-logical-resize@2.0.1(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-logical-viewport-units@2.0.10(postcss@8.5.6)': + dependencies: + '@csstools/css-tokenizer': 2.3.2 + '@csstools/utilities': 1.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-media-minmax@1.1.7(postcss@8.5.6)': + dependencies: + '@csstools/css-calc': 1.2.3(@csstools/css-parser-algorithms@2.7.0(@csstools/css-tokenizer@2.3.2))(@csstools/css-tokenizer@2.3.2) + '@csstools/css-parser-algorithms': 2.7.0(@csstools/css-tokenizer@2.3.2) + '@csstools/css-tokenizer': 2.3.2 + '@csstools/media-query-list-parser': 2.1.12(@csstools/css-parser-algorithms@2.7.0(@csstools/css-tokenizer@2.3.2))(@csstools/css-tokenizer@2.3.2) + postcss: 8.5.6 + + '@csstools/postcss-media-queries-aspect-ratio-number-values@2.0.10(postcss@8.5.6)': + dependencies: + '@csstools/css-parser-algorithms': 2.7.0(@csstools/css-tokenizer@2.3.2) + '@csstools/css-tokenizer': 2.3.2 + '@csstools/media-query-list-parser': 2.1.12(@csstools/css-parser-algorithms@2.7.0(@csstools/css-tokenizer@2.3.2))(@csstools/css-tokenizer@2.3.2) + postcss: 8.5.6 + + '@csstools/postcss-nested-calc@3.0.2(postcss@8.5.6)': + dependencies: + '@csstools/utilities': 1.0.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-normalize-display-values@3.0.2(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-oklab-function@3.0.17(postcss@8.5.6)': + dependencies: + '@csstools/css-color-parser': 2.0.3(@csstools/css-parser-algorithms@2.7.0(@csstools/css-tokenizer@2.3.2))(@csstools/css-tokenizer@2.3.2) + '@csstools/css-parser-algorithms': 2.7.0(@csstools/css-tokenizer@2.3.2) + '@csstools/css-tokenizer': 2.3.2 + '@csstools/postcss-progressive-custom-properties': 3.2.0(postcss@8.5.6) + '@csstools/utilities': 1.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-progressive-custom-properties@3.2.0(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-relative-color-syntax@2.0.17(postcss@8.5.6)': + dependencies: + '@csstools/css-color-parser': 2.0.3(@csstools/css-parser-algorithms@2.7.0(@csstools/css-tokenizer@2.3.2))(@csstools/css-tokenizer@2.3.2) + '@csstools/css-parser-algorithms': 2.7.0(@csstools/css-tokenizer@2.3.2) + '@csstools/css-tokenizer': 2.3.2 + '@csstools/postcss-progressive-custom-properties': 3.2.0(postcss@8.5.6) + '@csstools/utilities': 1.0.0(postcss@8.5.6) + postcss: 8.5.6 + + '@csstools/postcss-scope-pseudo-class@3.0.1(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + '@csstools/postcss-stepped-value-functions@3.0.9(postcss@8.5.6)': + dependencies: + '@csstools/css-calc': 1.2.3(@csstools/css-parser-algorithms@2.7.0(@csstools/css-tokenizer@2.3.2))(@csstools/css-tokenizer@2.3.2) + '@csstools/css-parser-algorithms': 2.7.0(@csstools/css-tokenizer@2.3.2) + '@csstools/css-tokenizer': 2.3.2 + postcss: 8.5.6 + + '@csstools/postcss-text-decoration-shorthand@3.0.7(postcss@8.5.6)': + dependencies: + '@csstools/color-helpers': 4.2.1 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + '@csstools/postcss-trigonometric-functions@3.0.9(postcss@8.5.6)': + dependencies: + '@csstools/css-calc': 1.2.3(@csstools/css-parser-algorithms@2.7.0(@csstools/css-tokenizer@2.3.2))(@csstools/css-tokenizer@2.3.2) + '@csstools/css-parser-algorithms': 2.7.0(@csstools/css-tokenizer@2.3.2) + '@csstools/css-tokenizer': 2.3.2 + postcss: 8.5.6 + + '@csstools/postcss-unset-value@3.0.1(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + + '@csstools/selector-resolve-nested@1.1.0(postcss-selector-parser@6.1.2)': + dependencies: + postcss-selector-parser: 6.1.2 + + '@csstools/selector-specificity@3.0.0(postcss-selector-parser@6.0.13)': + dependencies: + postcss-selector-parser: 6.0.13 + + '@csstools/selector-specificity@3.1.1(postcss-selector-parser@6.1.2)': + dependencies: + postcss-selector-parser: 6.1.2 + + '@csstools/utilities@1.0.0(postcss@8.5.6)': + dependencies: + postcss: 8.5.6 + + '@discoveryjs/json-ext@0.5.7': {} + + '@emnapi/core@1.5.0': + dependencies: + '@emnapi/wasi-threads': 1.1.0 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.5.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.1.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/android-arm64@0.18.20': + optional: true + + '@esbuild/android-arm@0.18.20': + optional: true + + '@esbuild/android-x64@0.18.20': + optional: true + + '@esbuild/darwin-arm64@0.18.20': + optional: true + + '@esbuild/darwin-x64@0.18.20': + optional: true + + '@esbuild/freebsd-arm64@0.18.20': + optional: true + + '@esbuild/freebsd-x64@0.18.20': + optional: true + + '@esbuild/linux-arm64@0.18.20': + optional: true + + '@esbuild/linux-arm@0.18.20': + optional: true + + '@esbuild/linux-ia32@0.18.20': + optional: true + + '@esbuild/linux-loong64@0.18.20': + optional: true + + '@esbuild/linux-mips64el@0.18.20': + optional: true + + '@esbuild/linux-ppc64@0.18.20': + optional: true + + '@esbuild/linux-riscv64@0.18.20': + optional: true + + '@esbuild/linux-s390x@0.18.20': + optional: true + + '@esbuild/linux-x64@0.18.20': + optional: true + + '@esbuild/netbsd-x64@0.18.20': + optional: true + + '@esbuild/openbsd-x64@0.18.20': + optional: true + + '@esbuild/sunos-x64@0.18.20': + optional: true + + '@esbuild/win32-arm64@0.18.20': + optional: true + + '@esbuild/win32-ia32@0.18.20': + optional: true + + '@esbuild/win32-x64@0.18.20': + optional: true + + '@eslint-community/eslint-utils@4.4.0(eslint@8.57.1)': + dependencies: + eslint: 8.57.1 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/eslint-utils@4.7.0(eslint@8.57.1)': + dependencies: + eslint: 8.57.1 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.9.0': {} + + '@eslint/eslintrc@2.1.4': + dependencies: + ajv: 6.12.6 + debug: 4.3.6 + espree: 9.6.1 + globals: 13.24.0 + ignore: 5.3.1 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@8.57.1': {} + + '@fastify/busboy@1.2.1': + dependencies: + text-decoding: 1.0.0 + optional: true + + '@firebase/analytics-compat@0.2.14(@firebase/app-compat@0.2.43)(@firebase/app@0.10.13)': + dependencies: + '@firebase/analytics': 0.10.8(@firebase/app@0.10.13) + '@firebase/analytics-types': 0.8.2 + '@firebase/app-compat': 0.2.43 + '@firebase/component': 0.6.9 + '@firebase/util': 1.10.0 + tslib: 2.6.2 + transitivePeerDependencies: + - '@firebase/app' + + '@firebase/analytics-types@0.8.2': {} + + '@firebase/analytics@0.10.8(@firebase/app@0.10.13)': + dependencies: + '@firebase/app': 0.10.13 + '@firebase/component': 0.6.9 + '@firebase/installations': 0.6.9(@firebase/app@0.10.13) + '@firebase/logger': 0.4.2 + '@firebase/util': 1.10.0 + tslib: 2.6.2 + + '@firebase/app-check-compat@0.3.15(@firebase/app-compat@0.2.43)(@firebase/app@0.10.13)': + dependencies: + '@firebase/app-check': 0.8.8(@firebase/app@0.10.13) + '@firebase/app-check-types': 0.5.2 + '@firebase/app-compat': 0.2.43 + '@firebase/component': 0.6.9 + '@firebase/logger': 0.4.2 + '@firebase/util': 1.10.0 + tslib: 2.6.2 + transitivePeerDependencies: + - '@firebase/app' + + '@firebase/app-check-interop-types@0.3.2': {} + + '@firebase/app-check-types@0.5.2': {} + + '@firebase/app-check@0.8.8(@firebase/app@0.10.13)': + dependencies: + '@firebase/app': 0.10.13 + '@firebase/component': 0.6.9 + '@firebase/logger': 0.4.2 + '@firebase/util': 1.10.0 + tslib: 2.6.2 + + '@firebase/app-compat@0.2.43': + dependencies: + '@firebase/app': 0.10.13 + '@firebase/component': 0.6.9 + '@firebase/logger': 0.4.2 + '@firebase/util': 1.10.0 + tslib: 2.6.2 + + '@firebase/app-types@0.8.1': + optional: true + + '@firebase/app-types@0.9.2': {} + + '@firebase/app@0.10.13': + dependencies: + '@firebase/component': 0.6.9 + '@firebase/logger': 0.4.2 + '@firebase/util': 1.10.0 + idb: 7.1.1 + tslib: 2.6.2 + + '@firebase/auth-compat@0.5.14(@firebase/app-compat@0.2.43)(@firebase/app-types@0.9.2)(@firebase/app@0.10.13)': + dependencies: + '@firebase/app-compat': 0.2.43 + '@firebase/auth': 1.7.9(@firebase/app@0.10.13) + '@firebase/auth-types': 0.12.2(@firebase/app-types@0.9.2)(@firebase/util@1.10.0) + '@firebase/component': 0.6.9 + '@firebase/util': 1.10.0 + tslib: 2.6.2 + undici: 6.19.7 + transitivePeerDependencies: + - '@firebase/app' + - '@firebase/app-types' + - '@react-native-async-storage/async-storage' + + '@firebase/auth-interop-types@0.1.7(@firebase/app-types@0.9.2)(@firebase/util@1.7.3)': + dependencies: + '@firebase/app-types': 0.9.2 + '@firebase/util': 1.7.3 + optional: true + + '@firebase/auth-interop-types@0.2.3': {} + + '@firebase/auth-types@0.12.2(@firebase/app-types@0.9.2)(@firebase/util@1.10.0)': + dependencies: + '@firebase/app-types': 0.9.2 + '@firebase/util': 1.10.0 + + '@firebase/auth@1.7.9(@firebase/app@0.10.13)': + dependencies: + '@firebase/app': 0.10.13 + '@firebase/component': 0.6.9 + '@firebase/logger': 0.4.2 + '@firebase/util': 1.10.0 + tslib: 2.6.2 + undici: 6.19.7 + + '@firebase/component@0.5.21': + dependencies: + '@firebase/util': 1.7.3 + tslib: 2.6.2 + optional: true + + '@firebase/component@0.6.9': + dependencies: + '@firebase/util': 1.10.0 + tslib: 2.6.2 + + '@firebase/data-connect@0.1.0(@firebase/app@0.10.13)': + dependencies: + '@firebase/app': 0.10.13 + '@firebase/auth-interop-types': 0.2.3 + '@firebase/component': 0.6.9 + '@firebase/logger': 0.4.2 + '@firebase/util': 1.10.0 + tslib: 2.6.2 + + '@firebase/database-compat@0.2.10(@firebase/app-types@0.9.2)': + dependencies: + '@firebase/component': 0.5.21 + '@firebase/database': 0.13.10(@firebase/app-types@0.9.2) + '@firebase/database-types': 0.9.17 + '@firebase/logger': 0.3.4 + '@firebase/util': 1.7.3 + tslib: 2.6.2 + transitivePeerDependencies: + - '@firebase/app-types' + optional: true + + '@firebase/database-compat@1.0.8': + dependencies: + '@firebase/component': 0.6.9 + '@firebase/database': 1.0.8 + '@firebase/database-types': 1.0.5 + '@firebase/logger': 0.4.2 + '@firebase/util': 1.10.0 + tslib: 2.6.2 + + '@firebase/database-types@0.9.17': + dependencies: + '@firebase/app-types': 0.8.1 + '@firebase/util': 1.7.3 + optional: true + + '@firebase/database-types@1.0.5': + dependencies: + '@firebase/app-types': 0.9.2 + '@firebase/util': 1.10.0 + + '@firebase/database@0.13.10(@firebase/app-types@0.9.2)': + dependencies: + '@firebase/auth-interop-types': 0.1.7(@firebase/app-types@0.9.2)(@firebase/util@1.7.3) + '@firebase/component': 0.5.21 + '@firebase/logger': 0.3.4 + '@firebase/util': 1.7.3 + faye-websocket: 0.11.4 + tslib: 2.6.2 + transitivePeerDependencies: + - '@firebase/app-types' + optional: true + + '@firebase/database@1.0.8': + dependencies: + '@firebase/app-check-interop-types': 0.3.2 + '@firebase/auth-interop-types': 0.2.3 + '@firebase/component': 0.6.9 + '@firebase/logger': 0.4.2 + '@firebase/util': 1.10.0 + faye-websocket: 0.11.4 + tslib: 2.6.2 + + '@firebase/firestore-compat@0.3.38(@firebase/app-compat@0.2.43)(@firebase/app-types@0.9.2)(@firebase/app@0.10.13)': + dependencies: + '@firebase/app-compat': 0.2.43 + '@firebase/component': 0.6.9 + '@firebase/firestore': 4.7.3(@firebase/app@0.10.13) + '@firebase/firestore-types': 3.0.2(@firebase/app-types@0.9.2)(@firebase/util@1.10.0) + '@firebase/util': 1.10.0 + tslib: 2.6.2 + transitivePeerDependencies: + - '@firebase/app' + - '@firebase/app-types' + + '@firebase/firestore-types@3.0.2(@firebase/app-types@0.9.2)(@firebase/util@1.10.0)': + dependencies: + '@firebase/app-types': 0.9.2 + '@firebase/util': 1.10.0 + + '@firebase/firestore@4.7.3(@firebase/app@0.10.13)': + dependencies: + '@firebase/app': 0.10.13 + '@firebase/component': 0.6.9 + '@firebase/logger': 0.4.2 + '@firebase/util': 1.10.0 + '@firebase/webchannel-wrapper': 1.0.1 + '@grpc/grpc-js': 1.9.15 + '@grpc/proto-loader': 0.7.10 + tslib: 2.6.2 + undici: 6.19.7 + + '@firebase/functions-compat@0.3.14(@firebase/app-compat@0.2.43)(@firebase/app@0.10.13)': + dependencies: + '@firebase/app-compat': 0.2.43 + '@firebase/component': 0.6.9 + '@firebase/functions': 0.11.8(@firebase/app@0.10.13) + '@firebase/functions-types': 0.6.2 + '@firebase/util': 1.10.0 + tslib: 2.6.2 + transitivePeerDependencies: + - '@firebase/app' + + '@firebase/functions-types@0.6.2': {} + + '@firebase/functions@0.11.8(@firebase/app@0.10.13)': + dependencies: + '@firebase/app': 0.10.13 + '@firebase/app-check-interop-types': 0.3.2 + '@firebase/auth-interop-types': 0.2.3 + '@firebase/component': 0.6.9 + '@firebase/messaging-interop-types': 0.2.2 + '@firebase/util': 1.10.0 + tslib: 2.6.2 + undici: 6.19.7 + + '@firebase/installations-compat@0.2.9(@firebase/app-compat@0.2.43)(@firebase/app-types@0.9.2)(@firebase/app@0.10.13)': + dependencies: + '@firebase/app-compat': 0.2.43 + '@firebase/component': 0.6.9 + '@firebase/installations': 0.6.9(@firebase/app@0.10.13) + '@firebase/installations-types': 0.5.2(@firebase/app-types@0.9.2) + '@firebase/util': 1.10.0 + tslib: 2.6.2 + transitivePeerDependencies: + - '@firebase/app' + - '@firebase/app-types' + + '@firebase/installations-types@0.5.2(@firebase/app-types@0.9.2)': + dependencies: + '@firebase/app-types': 0.9.2 + + '@firebase/installations@0.6.9(@firebase/app@0.10.13)': + dependencies: + '@firebase/app': 0.10.13 + '@firebase/component': 0.6.9 + '@firebase/util': 1.10.0 + idb: 7.1.1 + tslib: 2.6.2 + + '@firebase/logger@0.3.4': + dependencies: + tslib: 2.6.2 + optional: true + + '@firebase/logger@0.4.2': + dependencies: + tslib: 2.6.2 + + '@firebase/messaging-compat@0.2.12(@firebase/app-compat@0.2.43)(@firebase/app@0.10.13)': + dependencies: + '@firebase/app-compat': 0.2.43 + '@firebase/component': 0.6.9 + '@firebase/messaging': 0.12.12(@firebase/app@0.10.13) + '@firebase/util': 1.10.0 + tslib: 2.6.2 + transitivePeerDependencies: + - '@firebase/app' + + '@firebase/messaging-interop-types@0.2.2': {} + + '@firebase/messaging@0.12.12(@firebase/app@0.10.13)': + dependencies: + '@firebase/app': 0.10.13 + '@firebase/component': 0.6.9 + '@firebase/installations': 0.6.9(@firebase/app@0.10.13) + '@firebase/messaging-interop-types': 0.2.2 + '@firebase/util': 1.10.0 + idb: 7.1.1 + tslib: 2.6.2 + + '@firebase/performance-compat@0.2.9(@firebase/app-compat@0.2.43)(@firebase/app@0.10.13)': + dependencies: + '@firebase/app-compat': 0.2.43 + '@firebase/component': 0.6.9 + '@firebase/logger': 0.4.2 + '@firebase/performance': 0.6.9(@firebase/app@0.10.13) + '@firebase/performance-types': 0.2.2 + '@firebase/util': 1.10.0 + tslib: 2.6.2 + transitivePeerDependencies: + - '@firebase/app' + + '@firebase/performance-types@0.2.2': {} + + '@firebase/performance@0.6.9(@firebase/app@0.10.13)': + dependencies: + '@firebase/app': 0.10.13 + '@firebase/component': 0.6.9 + '@firebase/installations': 0.6.9(@firebase/app@0.10.13) + '@firebase/logger': 0.4.2 + '@firebase/util': 1.10.0 + tslib: 2.6.2 + + '@firebase/remote-config-compat@0.2.9(@firebase/app-compat@0.2.43)(@firebase/app@0.10.13)': + dependencies: + '@firebase/app-compat': 0.2.43 + '@firebase/component': 0.6.9 + '@firebase/logger': 0.4.2 + '@firebase/remote-config': 0.4.9(@firebase/app@0.10.13) + '@firebase/remote-config-types': 0.3.2 + '@firebase/util': 1.10.0 + tslib: 2.6.2 + transitivePeerDependencies: + - '@firebase/app' + + '@firebase/remote-config-types@0.3.2': {} + + '@firebase/remote-config@0.4.9(@firebase/app@0.10.13)': + dependencies: + '@firebase/app': 0.10.13 + '@firebase/component': 0.6.9 + '@firebase/installations': 0.6.9(@firebase/app@0.10.13) + '@firebase/logger': 0.4.2 + '@firebase/util': 1.10.0 + tslib: 2.6.2 + + '@firebase/storage-compat@0.3.12(@firebase/app-compat@0.2.43)(@firebase/app-types@0.9.2)(@firebase/app@0.10.13)': + dependencies: + '@firebase/app-compat': 0.2.43 + '@firebase/component': 0.6.9 + '@firebase/storage': 0.13.2(@firebase/app@0.10.13) + '@firebase/storage-types': 0.8.2(@firebase/app-types@0.9.2)(@firebase/util@1.10.0) + '@firebase/util': 1.10.0 + tslib: 2.6.2 + transitivePeerDependencies: + - '@firebase/app' + - '@firebase/app-types' + + '@firebase/storage-types@0.8.2(@firebase/app-types@0.9.2)(@firebase/util@1.10.0)': + dependencies: + '@firebase/app-types': 0.9.2 + '@firebase/util': 1.10.0 + + '@firebase/storage@0.13.2(@firebase/app@0.10.13)': + dependencies: + '@firebase/app': 0.10.13 + '@firebase/component': 0.6.9 + '@firebase/util': 1.10.0 + tslib: 2.6.2 + undici: 6.19.7 + + '@firebase/util@1.10.0': + dependencies: + tslib: 2.6.2 + + '@firebase/util@1.7.3': + dependencies: + tslib: 2.6.2 + optional: true + + '@firebase/vertexai-preview@0.0.4(@firebase/app-types@0.9.2)(@firebase/app@0.10.13)': + dependencies: + '@firebase/app': 0.10.13 + '@firebase/app-check-interop-types': 0.3.2 + '@firebase/app-types': 0.9.2 + '@firebase/component': 0.6.9 + '@firebase/logger': 0.4.2 + '@firebase/util': 1.10.0 + tslib: 2.6.2 + + '@firebase/webchannel-wrapper@1.0.1': {} + + '@gar/promisify@1.1.3': {} + + '@google-cloud/firestore@4.15.1': + dependencies: + fast-deep-equal: 3.1.3 + functional-red-black-tree: 1.0.1 + google-gax: 2.30.5 + protobufjs: 6.11.4 + transitivePeerDependencies: + - encoding + - supports-color + optional: true + + '@google-cloud/paginator@3.0.7': + dependencies: + arrify: 2.0.1 + extend: 3.0.2 + optional: true + + '@google-cloud/projectify@2.1.1': + optional: true + + '@google-cloud/promisify@2.0.4': + optional: true + + '@google-cloud/storage@5.20.5': + dependencies: + '@google-cloud/paginator': 3.0.7 + '@google-cloud/projectify': 2.1.1 + '@google-cloud/promisify': 2.0.4 + abort-controller: 3.0.0 + arrify: 2.0.1 + async-retry: 1.3.3 + compressible: 2.0.18 + configstore: 5.0.1 + duplexify: 4.1.2 + ent: 2.2.0 + extend: 3.0.2 + gaxios: 4.3.3 + google-auth-library: 7.14.1 + hash-stream-validation: 0.2.4 + mime: 3.0.0 + mime-types: 2.1.35 + p-limit: 3.1.0 + pumpify: 2.0.1 + retry-request: 4.2.2 + stream-events: 1.0.5 + teeny-request: 7.2.0 + uuid: 8.3.2 + xdg-basedir: 4.0.0 + transitivePeerDependencies: + - encoding + - supports-color + optional: true + + '@grpc/grpc-js@1.6.12': + dependencies: + '@grpc/proto-loader': 0.7.10 + '@types/node': 25.1.0 + optional: true + + '@grpc/grpc-js@1.9.15': + dependencies: + '@grpc/proto-loader': 0.7.10 + '@types/node': 25.1.0 + + '@grpc/proto-loader@0.6.13': + dependencies: + '@types/long': 4.0.2 + lodash.camelcase: 4.3.0 + long: 4.0.0 + protobufjs: 6.11.4 + yargs: 16.2.0 + optional: true + + '@grpc/proto-loader@0.7.10': + dependencies: + lodash.camelcase: 4.3.0 + long: 5.2.3 + protobufjs: 7.2.5 + yargs: 17.7.2 + + '@humanwhocodes/config-array@0.13.0': + dependencies: + '@humanwhocodes/object-schema': 2.0.3 + debug: 4.3.6 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/object-schema@2.0.3': {} + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.2 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@istanbuljs/load-nyc-config@1.1.0': + dependencies: + camelcase: 5.3.1 + find-up: 4.1.0 + get-package-type: 0.1.0 + js-yaml: 3.14.1 + resolve-from: 5.0.0 + + '@istanbuljs/schema@0.1.3': {} + + '@jest/console@30.2.0': + dependencies: + '@jest/types': 30.2.0 + '@types/node': 25.1.0 + chalk: 4.1.2 + jest-message-util: 30.2.0 + jest-util: 30.2.0 + slash: 3.0.0 + + '@jest/core@30.2.0': dependencies: - '@jest/console': 27.5.1 - '@jest/reporters': 27.5.1 - '@jest/test-result': 27.5.1 - '@jest/transform': 27.5.1 - '@jest/types': 27.5.1 - '@types/node': 20.8.0 + '@jest/console': 30.2.0 + '@jest/pattern': 30.0.1 + '@jest/reporters': 30.2.0 + '@jest/test-result': 30.2.0 + '@jest/transform': 30.2.0 + '@jest/types': 30.2.0 + '@types/node': 25.1.0 ansi-escapes: 4.3.2 chalk: 4.1.2 - emittery: 0.8.1 - exit: 0.1.2 + ci-info: 4.3.0 + exit-x: 0.2.2 graceful-fs: 4.2.11 - jest-changed-files: 27.5.1 - jest-config: 27.5.1(ts-node@10.9.1) - jest-haste-map: 27.5.1 - jest-message-util: 27.5.1 - jest-regex-util: 27.5.1 - jest-resolve: 27.5.1 - jest-resolve-dependencies: 27.5.1 - jest-runner: 27.5.1 - jest-runtime: 27.5.1 - jest-snapshot: 27.5.1 - jest-util: 27.5.1 - jest-validate: 27.5.1 - jest-watcher: 27.5.1 - micromatch: 4.0.5 - rimraf: 3.0.2 + jest-changed-files: 30.2.0 + jest-config: 30.2.0(@types/node@25.1.0) + jest-haste-map: 30.2.0 + jest-message-util: 30.2.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.2.0 + jest-resolve-dependencies: 30.2.0 + jest-runner: 30.2.0 + jest-runtime: 30.2.0 + jest-snapshot: 30.2.0 + jest-util: 30.2.0 + jest-validate: 30.2.0 + jest-watcher: 30.2.0 + micromatch: 4.0.8 + pretty-format: 30.2.0 slash: 3.0.0 - strip-ansi: 6.0.1 transitivePeerDependencies: - - bufferutil - - canvas + - babel-plugin-macros + - esbuild-register - supports-color - ts-node - - utf-8-validate - dev: true - - /@jest/environment@27.5.1: - resolution: - { - integrity: sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - dependencies: - '@jest/fake-timers': 27.5.1 - '@jest/types': 27.5.1 - '@types/node': 20.8.0 - jest-mock: 27.5.1 - dev: true - - /@jest/fake-timers@27.5.1: - resolution: - { - integrity: sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - dependencies: - '@jest/types': 27.5.1 - '@sinonjs/fake-timers': 8.1.0 - '@types/node': 20.8.0 - jest-message-util: 27.5.1 - jest-mock: 27.5.1 - jest-util: 27.5.1 - dev: true - - /@jest/globals@27.5.1: - resolution: - { - integrity: sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - dependencies: - '@jest/environment': 27.5.1 - '@jest/types': 27.5.1 - expect: 27.5.1 - dev: true - - /@jest/reporters@27.5.1: - resolution: - { - integrity: sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true + + '@jest/diff-sequences@30.0.1': {} + + '@jest/environment-jsdom-abstract@30.2.0(jsdom@26.1.0)': + dependencies: + '@jest/environment': 30.2.0 + '@jest/fake-timers': 30.2.0 + '@jest/types': 30.2.0 + '@types/jsdom': 21.1.7 + '@types/node': 25.1.0 + jest-mock: 30.2.0 + jest-util: 30.2.0 + jsdom: 26.1.0 + + '@jest/environment@30.2.0': + dependencies: + '@jest/fake-timers': 30.2.0 + '@jest/types': 30.2.0 + '@types/node': 25.1.0 + jest-mock: 30.2.0 + + '@jest/expect-utils@30.2.0': + dependencies: + '@jest/get-type': 30.1.0 + + '@jest/expect@30.2.0': + dependencies: + expect: 30.2.0 + jest-snapshot: 30.2.0 + transitivePeerDependencies: + - supports-color + + '@jest/fake-timers@30.2.0': + dependencies: + '@jest/types': 30.2.0 + '@sinonjs/fake-timers': 13.0.5 + '@types/node': 25.1.0 + jest-message-util: 30.2.0 + jest-mock: 30.2.0 + jest-util: 30.2.0 + + '@jest/get-type@30.1.0': {} + + '@jest/globals@30.2.0': + dependencies: + '@jest/environment': 30.2.0 + '@jest/expect': 30.2.0 + '@jest/types': 30.2.0 + jest-mock: 30.2.0 + transitivePeerDependencies: + - supports-color + + '@jest/pattern@30.0.1': + dependencies: + '@types/node': 25.1.0 + jest-regex-util: 30.0.1 + + '@jest/reporters@30.2.0': dependencies: '@bcoe/v8-coverage': 0.2.3 - '@jest/console': 27.5.1 - '@jest/test-result': 27.5.1 - '@jest/transform': 27.5.1 - '@jest/types': 27.5.1 - '@types/node': 20.8.0 + '@jest/console': 30.2.0 + '@jest/test-result': 30.2.0 + '@jest/transform': 30.2.0 + '@jest/types': 30.2.0 + '@jridgewell/trace-mapping': 0.3.31 + '@types/node': 25.1.0 chalk: 4.1.2 collect-v8-coverage: 1.0.2 - exit: 0.1.2 - glob: 7.2.3 + exit-x: 0.2.2 + glob: 10.4.5 graceful-fs: 4.2.11 - istanbul-lib-coverage: 3.2.0 - istanbul-lib-instrument: 5.2.1 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-instrument: 6.0.3 istanbul-lib-report: 3.0.1 - istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.6 - jest-haste-map: 27.5.1 - jest-resolve: 27.5.1 - jest-util: 27.5.1 - jest-worker: 27.5.1 + istanbul-lib-source-maps: 5.0.6 + istanbul-reports: 3.2.0 + jest-message-util: 30.2.0 + jest-util: 30.2.0 + jest-worker: 30.2.0 slash: 3.0.0 - source-map: 0.6.1 string-length: 4.0.2 - terminal-link: 2.1.1 - v8-to-istanbul: 8.1.1 + v8-to-istanbul: 9.3.0 transitivePeerDependencies: - supports-color - dev: true - /@jest/schemas@29.6.3: - resolution: - { - integrity: sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==, - } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + '@jest/schemas@29.6.3': dependencies: '@sinclair/typebox': 0.27.8 - dev: true - /@jest/source-map@27.5.1: - resolution: - { - integrity: sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } + '@jest/schemas@30.0.5': + dependencies: + '@sinclair/typebox': 0.34.41 + + '@jest/snapshot-utils@30.2.0': + dependencies: + '@jest/types': 30.2.0 + chalk: 4.1.2 + graceful-fs: 4.2.11 + natural-compare: 1.4.0 + + '@jest/source-map@30.0.1': dependencies: + '@jridgewell/trace-mapping': 0.3.31 callsites: 3.1.0 graceful-fs: 4.2.11 - source-map: 0.6.1 - dev: true - - /@jest/test-result@27.5.1: - resolution: - { - integrity: sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - dependencies: - '@jest/console': 27.5.1 - '@jest/types': 27.5.1 - '@types/istanbul-lib-coverage': 2.0.4 + + '@jest/test-result@30.2.0': + dependencies: + '@jest/console': 30.2.0 + '@jest/types': 30.2.0 + '@types/istanbul-lib-coverage': 2.0.6 collect-v8-coverage: 1.0.2 - dev: true - /@jest/test-sequencer@27.5.1: - resolution: - { - integrity: sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } + '@jest/test-sequencer@30.2.0': dependencies: - '@jest/test-result': 27.5.1 - graceful-fs: 4.2.11 - jest-haste-map: 27.5.1 - jest-runtime: 27.5.1 - transitivePeerDependencies: - - supports-color - dev: true - - /@jest/transform@27.5.1: - resolution: - { - integrity: sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - dependencies: - '@babel/core': 7.23.0 - '@jest/types': 27.5.1 - babel-plugin-istanbul: 6.1.1 - chalk: 4.1.2 - convert-source-map: 1.9.0 - fast-json-stable-stringify: 2.1.0 + '@jest/test-result': 30.2.0 graceful-fs: 4.2.11 - jest-haste-map: 27.5.1 - jest-regex-util: 27.5.1 - jest-util: 27.5.1 - micromatch: 4.0.5 - pirates: 4.0.6 + jest-haste-map: 30.2.0 slash: 3.0.0 - source-map: 0.6.1 - write-file-atomic: 3.0.3 - transitivePeerDependencies: - - supports-color - dev: true - /@jest/transform@29.7.0: - resolution: - { - integrity: sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==, - } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + '@jest/transform@30.2.0': dependencies: - '@babel/core': 7.23.0 - '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.19 - babel-plugin-istanbul: 6.1.1 + '@babel/core': 7.28.4 + '@jest/types': 30.2.0 + '@jridgewell/trace-mapping': 0.3.31 + babel-plugin-istanbul: 7.0.1 chalk: 4.1.2 convert-source-map: 2.0.0 fast-json-stable-stringify: 2.1.0 graceful-fs: 4.2.11 - jest-haste-map: 29.7.0 - jest-regex-util: 29.6.3 - jest-util: 29.7.0 - micromatch: 4.0.5 - pirates: 4.0.6 + jest-haste-map: 30.2.0 + jest-regex-util: 30.0.1 + jest-util: 30.2.0 + micromatch: 4.0.8 + pirates: 4.0.7 slash: 3.0.0 - write-file-atomic: 4.0.2 + write-file-atomic: 5.0.1 transitivePeerDependencies: - supports-color - dev: true - - /@jest/types@27.5.1: - resolution: - { - integrity: sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - dependencies: - '@types/istanbul-lib-coverage': 2.0.4 - '@types/istanbul-reports': 3.0.2 - '@types/node': 20.8.0 - '@types/yargs': 16.0.6 - chalk: 4.1.2 - dev: true - /@jest/types@29.6.3: - resolution: - { - integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==, - } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + '@jest/types@29.6.3': dependencies: '@jest/schemas': 29.6.3 - '@types/istanbul-lib-coverage': 2.0.4 - '@types/istanbul-reports': 3.0.2 - '@types/node': 20.8.0 - '@types/yargs': 17.0.26 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 25.1.0 + '@types/yargs': 17.0.33 + chalk: 4.1.2 + + '@jest/types@30.2.0': + dependencies: + '@jest/pattern': 30.0.1 + '@jest/schemas': 30.0.5 + '@types/istanbul-lib-coverage': 2.0.6 + '@types/istanbul-reports': 3.0.4 + '@types/node': 25.1.0 + '@types/yargs': 17.0.33 chalk: 4.1.2 - dev: true - /@jridgewell/gen-mapping@0.3.3: - resolution: - { - integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==, - } - engines: { node: '>=6.0.0' } + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/gen-mapping@0.3.3': dependencies: '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.15 - '@jridgewell/trace-mapping': 0.3.19 - - /@jridgewell/resolve-uri@3.1.1: - resolution: - { - integrity: sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==, - } - engines: { node: '>=6.0.0' } - - /@jridgewell/set-array@1.1.2: - resolution: - { - integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==, - } - engines: { node: '>=6.0.0' } - - /@jridgewell/source-map@0.3.5: - resolution: - { - integrity: sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ==, - } + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/gen-mapping@0.3.5': dependencies: - '@jridgewell/gen-mapping': 0.3.3 - '@jridgewell/trace-mapping': 0.3.19 - - /@jridgewell/sourcemap-codec@1.4.15: - resolution: - { - integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==, - } - - /@jridgewell/trace-mapping@0.3.19: - resolution: - { - integrity: sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==, - } - dependencies: - '@jridgewell/resolve-uri': 3.1.1 - '@jridgewell/sourcemap-codec': 1.4.15 - - /@jridgewell/trace-mapping@0.3.9: - resolution: - { - integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==, - } - dependencies: - '@jridgewell/resolve-uri': 3.1.1 - '@jridgewell/sourcemap-codec': 1.4.15 - dev: true - - /@kurkle/color@0.3.2: - resolution: - { - integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==, - } - dev: false - - /@mdi/js@7.3.67: - resolution: - { - integrity: sha512-MnRjknFqpTC6FifhGHjZ0+QYq2bAkZFQqIj8JA2AdPZbBxUvr8QSgB2yPAJ8/ob/XkR41xlg5majDR3c1JP1hw==, - } - dev: false - - /@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1: - resolution: - { - integrity: sha512-54/JRvkLIzzDWshCWfuhadfrfZVPiElY8Fcgmg1HroEly/EDSszzhBAsarCux+D/kOslTRquNzuyGSmUSTTHGg==, - } + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/source-map@0.3.11': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@jsonjoy.com/base64@1.1.2(tslib@2.6.2)': + dependencies: + tslib: 2.6.2 + + '@jsonjoy.com/json-pack@1.0.4(tslib@2.6.2)': + dependencies: + '@jsonjoy.com/base64': 1.1.2(tslib@2.6.2) + '@jsonjoy.com/util': 1.2.0(tslib@2.6.2) + hyperdyperid: 1.2.0 + thingies: 1.21.0(tslib@2.6.2) + tslib: 2.6.2 + + '@jsonjoy.com/util@1.2.0(tslib@2.6.2)': + dependencies: + tslib: 2.6.2 + + '@kurkle/color@0.3.4': {} + + '@mdi/js@7.4.47': {} + + '@napi-rs/wasm-runtime@0.2.12': + dependencies: + '@emnapi/core': 1.5.0 + '@emnapi/runtime': 1.5.0 + '@tybys/wasm-util': 0.10.1 + optional: true + + '@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1': dependencies: eslint-scope: 5.1.1 - dev: true - /@nodelib/fs.scandir@2.1.5: - resolution: - { - integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==, - } - engines: { node: '>= 8' } + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 run-parallel: 1.2.0 - /@nodelib/fs.stat@2.0.5: - resolution: - { - integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==, - } - engines: { node: '>= 8' } + '@nodelib/fs.stat@2.0.5': {} - /@nodelib/fs.walk@1.2.8: - resolution: - { - integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==, - } - engines: { node: '>= 8' } + '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 fastq: 1.15.0 - /@npmcli/fs@1.1.1: - resolution: - { - integrity: sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==, - } + '@npmcli/fs@1.1.1': dependencies: '@gar/promisify': 1.1.3 - semver: 7.5.4 - dev: false - - /@npmcli/move-file@1.1.2: - resolution: - { - integrity: sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==, - } - engines: { node: '>=10' } - deprecated: This functionality has been moved to @npmcli/fs + semver: 7.7.3 + + '@npmcli/move-file@1.1.2': dependencies: mkdirp: 1.0.4 rimraf: 3.0.2 - dev: false - - /@nuxt/babel-preset-app@2.17.2(vue@2.7.15): - resolution: - { - integrity: sha512-LJmL19mlzcwBOcyjiuwsgj0WSUHQglEEgZ2C0IE+5GfKblyVnzHi8PLBr4M8U78QzRkMoZXdRg7bIyW4VBANCQ==, - } - engines: { node: ^14.18.0 || >=16.10.0 } - dependencies: - '@babel/compat-data': 7.23.2 - '@babel/core': 7.23.2 - '@babel/helper-compilation-targets': 7.22.15 - '@babel/helper-module-imports': 7.22.15 - '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.23.2) - '@babel/plugin-proposal-decorators': 7.23.2(@babel/core@7.23.2) - '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.23.2) - '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.23.2) - '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.23.2) - '@babel/plugin-proposal-private-property-in-object': 7.21.11(@babel/core@7.23.2) - '@babel/plugin-transform-runtime': 7.23.2(@babel/core@7.23.2) - '@babel/preset-env': 7.23.2(@babel/core@7.23.2) - '@babel/runtime': 7.23.2 - '@vue/babel-preset-jsx': 1.4.0(@babel/core@7.23.2)(vue@2.7.15) - core-js: 3.33.2 - core-js-compat: 3.33.2 - regenerator-runtime: 0.14.0 + + '@nuxt/babel-preset-app@2.18.1(vue@2.7.16)': + dependencies: + '@babel/compat-data': 7.24.7 + '@babel/core': 7.24.7 + '@babel/helper-compilation-targets': 7.24.7 + '@babel/helper-module-imports': 7.24.7 + '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.24.7) + '@babel/plugin-proposal-decorators': 7.24.7(@babel/core@7.24.7) + '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.24.7) + '@babel/plugin-proposal-optional-chaining': 7.21.0(@babel/core@7.24.7) + '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.24.7) + '@babel/plugin-proposal-private-property-in-object': 7.21.11(@babel/core@7.24.7) + '@babel/plugin-transform-runtime': 7.24.7(@babel/core@7.24.7) + '@babel/preset-env': 7.24.7(@babel/core@7.24.7) + '@babel/runtime': 7.24.7 + '@vue/babel-preset-jsx': 1.4.0(@babel/core@7.24.7)(vue@2.7.16) + core-js: 3.48.0 + core-js-compat: 3.37.1 + regenerator-runtime: 0.14.1 transitivePeerDependencies: - supports-color - vue - dev: false - /@nuxt/builder@2.17.2(babel-core@7.0.0-bridge.0)(prettier@3.0.3)(typescript@4.9.5)(vue@2.7.15): - resolution: - { - integrity: sha512-6NYsLD2Ss3QjJ8vgNVhdXvn44CPVLotnNAnq3/WGpkxRloebqBxjUTzLQUjsZ/U7STH00TWCUAkhq8zpSzEogw==, - } - engines: { node: ^14.18.0 || >=16.10.0 } + '@nuxt/builder@2.18.1(babel-core@7.0.0-bridge.0(@babel/core@7.28.4))(ejs@3.1.10)(handlebars@4.7.8)(prettier@3.8.1)(typescript@4.9.5)(vue@2.7.16)': dependencies: '@nuxt/devalue': 2.0.2 - '@nuxt/utils': 2.17.2 - '@nuxt/vue-app': 2.17.2 - '@nuxt/webpack': 2.17.2(babel-core@7.0.0-bridge.0)(prettier@3.0.3)(typescript@4.9.5)(vue@2.7.15) + '@nuxt/utils': 2.18.1 + '@nuxt/vue-app': 2.18.1 + '@nuxt/webpack': 2.18.1(babel-core@7.0.0-bridge.0(@babel/core@7.28.4))(ejs@3.1.10)(handlebars@4.7.8)(prettier@3.8.1)(typescript@4.9.5)(vue@2.7.16) chalk: 4.1.2 - chokidar: 3.5.3 + chokidar: 3.6.0 consola: 3.2.3 - fs-extra: 10.1.0 + fs-extra: 11.2.0 glob: 8.1.0 hash-sum: 2.0.0 - ignore: 5.2.4 + ignore: 5.3.1 lodash: 4.17.21 pify: 5.0.0 - serialize-javascript: 6.0.1 + serialize-javascript: 6.0.2 upath: 2.0.1 transitivePeerDependencies: - '@vue/compiler-sfc' @@ -4502,408 +11705,329 @@ packages: - webpack-cli - webpack-command - whiskers - dev: false - - /@nuxt/cli@2.17.2: - resolution: - { - integrity: sha512-jEDxBqrKINJrJunewamghqOFcR/6FhDU6kGlO10kWKvE5QzTRQUKLUgBQVag7yRqnnMjL5NcZoUC4iUeBT/63Q==, - } - engines: { node: ^14.18.0 || >=16.10.0 } - hasBin: true + + '@nuxt/cli@2.18.1': dependencies: - '@nuxt/config': 2.17.2 - '@nuxt/utils': 2.17.2 + '@nuxt/config': 2.18.1 + '@nuxt/utils': 2.18.1 boxen: 5.1.2 chalk: 4.1.2 compression: 1.7.4 connect: 3.7.0 consola: 3.2.3 crc: 4.3.2 - defu: 6.1.2 - destr: 2.0.1 + defu: 6.1.4 + destr: 2.0.3 execa: 5.1.1 exit: 0.1.2 - fs-extra: 10.1.0 + fs-extra: 11.2.0 globby: 11.1.0 - hable: 3.0.0 + hookable: 4.4.1 lodash: 4.17.21 minimist: 1.2.8 opener: 1.5.2 pretty-bytes: 5.6.0 - semver: 7.5.4 - serve-static: 1.15.0 - std-env: 3.4.3 + semver: 7.7.3 + serve-static: 1.16.2 + std-env: 3.7.0 upath: 2.0.1 wrap-ansi: 7.0.0 transitivePeerDependencies: - buffer - supports-color - dev: false - /@nuxt/components@2.2.1(consola@3.2.3): - resolution: - { - integrity: sha512-r1LHUzifvheTnJtYrMuA+apgsrEJbxcgFKIimeXKb+jl8TnPWdV3egmrxBCaDJchrtY/wmHyP47tunsft7AWwg==, - } - peerDependencies: - consola: '*' + '@nuxt/components@2.2.1(consola@3.2.3)': dependencies: chalk: 4.1.2 - chokidar: 3.5.3 + chokidar: 3.6.0 consola: 3.2.3 glob: 7.2.3 globby: 11.1.0 scule: 0.2.1 - semver: 7.5.4 + semver: 7.7.3 upath: 2.0.1 - vue-template-compiler: 2.7.15 - dev: false + vue-template-compiler: 2.7.16 - /@nuxt/config@2.17.2: - resolution: - { - integrity: sha512-e40+37nwLDnf7DGOfoK1D1GkWvNh4sNkZdgKGDkJZCVBkj9a6dgKwCdBvwtxphbJHhI6X05YG/K3mB41t8cEvg==, - } - engines: { node: ^14.18.0 || >=16.10.0 } + '@nuxt/config@2.18.1': dependencies: - '@nuxt/utils': 2.17.2 + '@nuxt/utils': 2.18.1 consola: 3.2.3 - defu: 6.1.2 - destr: 2.0.1 - dotenv: 16.3.1 + defu: 6.1.4 + destr: 2.0.3 + dotenv: 16.6.1 lodash: 4.17.21 - rc9: 2.1.1 - std-env: 3.4.3 - ufo: 1.3.1 - dev: false - - /@nuxt/core@2.17.2: - resolution: - { - integrity: sha512-DNJZ7dsMQShdXLQZg2ruIpk10dbU1HrgEXMD4DYmSBUPN0fT1LyWp6e5xIkDmyPXcsHeQYDQPezYddM9LhQn+A==, - } - engines: { node: ^14.18.0 || >=16.10.0 } - dependencies: - '@nuxt/config': 2.17.2 - '@nuxt/server': 2.17.2 - '@nuxt/utils': 2.17.2 + rc9: 2.1.2 + std-env: 3.7.0 + ufo: 1.6.1 + + '@nuxt/core@2.18.1': + dependencies: + '@nuxt/config': 2.18.1 + '@nuxt/server': 2.18.1 + '@nuxt/utils': 2.18.1 consola: 3.2.3 - fs-extra: 10.1.0 - hable: 3.0.0 + fs-extra: 11.2.0 hash-sum: 2.0.0 + hookable: 4.4.1 lodash: 4.17.21 transitivePeerDependencies: - supports-color - dev: false - - /@nuxt/devalue@2.0.2: - resolution: - { - integrity: sha512-GBzP8zOc7CGWyFQS6dv1lQz8VVpz5C2yRszbXufwG/9zhStTIH50EtD87NmWbTMwXDvZLNg8GIpb1UFdH93JCA==, - } - dev: false - - /@nuxt/friendly-errors-webpack-plugin@2.5.2(webpack@4.47.0): - resolution: - { - integrity: sha512-LLc+90lnxVbpKkMqk5z1EWpXoODhc6gRkqqXJCInJwF5xabHAE7biFvbULfvTRmtaTzAaP8IV4HQDLUgeAUTTw==, - } - engines: { node: '>=8.0.0', npm: '>=5.0.0' } - peerDependencies: - webpack: ^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 + + '@nuxt/devalue@2.0.2': {} + + '@nuxt/friendly-errors-webpack-plugin@2.6.0(webpack@4.47.0)': dependencies: chalk: 2.4.2 - consola: 2.15.3 + consola: 3.2.3 error-stack-parser: 2.1.4 string-width: 4.2.3 webpack: 4.47.0 - dev: false - /@nuxt/generator@2.17.2: - resolution: - { - integrity: sha512-nYkxUtp+geqPYH1KngE+0L9vPVS+zZ6O9cm3yzSqy/KuzPxQYYK+YAgC+cORo3pnxr4MNvRTj3vpPBvEnt2KVA==, - } - engines: { node: ^14.18.0 || >=16.10.0 } + '@nuxt/generator@2.18.1': dependencies: - '@nuxt/utils': 2.17.2 + '@nuxt/utils': 2.18.1 chalk: 4.1.2 consola: 3.2.3 - defu: 6.1.2 + defu: 6.1.4 devalue: 2.0.1 - fs-extra: 10.1.0 - html-minifier: 4.0.0 - node-html-parser: 6.1.10 - ufo: 1.3.1 - dev: false - - /@nuxt/kit@3.7.4: - resolution: - { - integrity: sha512-/S5abZL62BITCvC/TY3KWA6N721U1Osln3cQdBb56XHIeafZCBVqTi92Xb0o7ovl72mMRhrKwRu7elzvz9oT/g==, - } - engines: { node: ^14.18.0 || >=16.10.0 } - dependencies: - '@nuxt/schema': 3.7.4 + fs-extra: 11.2.0 + html-minifier-terser: 7.2.0 + node-html-parser: 6.1.13 + ufo: 1.6.1 + + '@nuxt/kit@3.12.2(rollup@3.30.0)': + dependencies: + '@nuxt/schema': 3.12.2(rollup@3.30.0) + c12: 1.11.1 + consola: 3.2.3 + defu: 6.1.4 + destr: 2.0.3 + globby: 14.0.2 + hash-sum: 2.0.0 + ignore: 5.3.1 + jiti: 1.21.6 + klona: 2.0.6 + knitwork: 1.1.0 + mlly: 1.7.1 + pathe: 1.1.2 + pkg-types: 1.1.2 + scule: 1.3.0 + semver: 7.7.3 + ufo: 1.6.1 + unctx: 2.3.1 + unimport: 3.7.2(rollup@3.30.0) + untyped: 1.4.2 + transitivePeerDependencies: + - magicast + - rollup + - supports-color + + '@nuxt/kit@3.7.4(rollup@3.30.0)': + dependencies: + '@nuxt/schema': 3.7.4(rollup@3.30.0) c12: 1.4.2 consola: 3.2.3 - defu: 6.1.2 + defu: 6.1.4 globby: 13.2.2 hash-sum: 2.0.0 - ignore: 5.2.4 + ignore: 5.3.1 jiti: 1.20.0 knitwork: 1.0.0 mlly: 1.4.2 - pathe: 1.1.1 - pkg-types: 1.0.3 + pathe: 1.1.2 + pkg-types: 1.1.2 scule: 1.0.0 - semver: 7.5.4 - ufo: 1.3.1 + semver: 7.7.3 + ufo: 1.6.1 unctx: 2.3.1 - unimport: 3.4.0 + unimport: 3.4.0(rollup@3.30.0) untyped: 1.4.0 transitivePeerDependencies: - rollup - supports-color - dev: true - /@nuxt/loading-screen@2.0.4: - resolution: - { - integrity: sha512-xpEDAoRu75tLUYCkUJCIvJkWJSuwr8pqomvQ+fkXpSrkxZ/9OzlBFjAbVdOAWTMj4aV/LVQso4vcEdircKeFIQ==, - } + '@nuxt/loading-screen@2.0.4': dependencies: connect: 3.7.0 defu: 5.0.1 get-port-please: 2.6.1 node-res: 5.0.1 - serve-static: 1.15.0 + serve-static: 1.16.2 transitivePeerDependencies: - supports-color - dev: false - - /@nuxt/opencollective@0.3.3: - resolution: - { - integrity: sha512-6IKCd+gP0HliixqZT/p8nW3tucD6Sv/u/eR2A9X4rxT/6hXlMzA4GZQzq4d2qnBAwSwGpmKyzkyTjNjrhaA25A==, - } - engines: { node: '>=8.0.0', npm: '>=5.0.0' } - hasBin: true + + '@nuxt/opencollective@0.4.0': dependencies: chalk: 4.1.2 - consola: 2.15.3 - node-fetch: 2.7.0 + consola: 3.2.3 + node-fetch-native: 1.6.7 + + '@nuxt/schema@3.12.2(rollup@3.30.0)': + dependencies: + compatx: 0.1.8 + consola: 3.2.3 + defu: 6.1.4 + hookable: 5.5.3 + pathe: 1.1.2 + pkg-types: 1.1.2 + scule: 1.3.0 + std-env: 3.7.0 + ufo: 1.6.1 + uncrypto: 0.1.3 + unimport: 3.7.2(rollup@3.30.0) + untyped: 1.4.2 transitivePeerDependencies: - - encoding - dev: false + - rollup + - supports-color - /@nuxt/schema@3.7.4: - resolution: - { - integrity: sha512-q6js+97vDha4Fa2x2kDVEuokJr+CGIh1TY2wZp2PLZ7NhG3XEeib7x9Hq8XE8B6pD0GKBRy3eRPPOY69gekBCw==, - } - engines: { node: ^14.18.0 || >=16.10.0 } + '@nuxt/schema@3.7.4(rollup@3.30.0)': dependencies: '@nuxt/ui-templates': 1.3.1 consola: 3.2.3 - defu: 6.1.2 + defu: 6.1.4 hookable: 5.5.3 - pathe: 1.1.1 - pkg-types: 1.0.3 + pathe: 1.1.2 + pkg-types: 1.1.2 postcss-import-resolver: 2.0.0 - std-env: 3.4.3 - ufo: 1.3.1 - unimport: 3.4.0 - untyped: 1.4.0 + std-env: 3.7.0 + ufo: 1.6.1 + unimport: 3.7.2(rollup@3.30.0) + untyped: 1.4.2 transitivePeerDependencies: - rollup - supports-color - dev: true - /@nuxt/server@2.17.2: - resolution: - { - integrity: sha512-PjIpxC9B7heH6f+uzPTAleConC8uhfmaPZaj+A96RXwxw/zAYHbv/QKFz4Q+Pw6v4aONOYsPZFBuLoMTGov3tA==, - } - engines: { node: ^14.18.0 || >=16.10.0 } + '@nuxt/server@2.18.1': dependencies: - '@nuxt/utils': 2.17.2 - '@nuxt/vue-renderer': 2.17.2 + '@nuxt/utils': 2.18.1 + '@nuxt/vue-renderer': 2.18.1 '@nuxtjs/youch': 4.2.3 compression: 1.7.4 connect: 3.7.0 consola: 3.2.3 etag: 1.8.1 fresh: 0.5.2 - fs-extra: 10.1.0 - ip: 1.1.8 - launch-editor-middleware: 2.6.1 + fs-extra: 11.2.0 + ip: 2.0.1 + launch-editor-middleware: 2.8.0 on-headers: 1.0.2 pify: 5.0.0 - serve-placeholder: 2.0.1 - serve-static: 1.15.0 + serve-placeholder: 2.0.2 + serve-static: 1.16.2 server-destroy: 1.0.1 - ufo: 1.3.1 + ufo: 1.6.1 transitivePeerDependencies: - supports-color - dev: false - /@nuxt/telemetry@1.4.1: - resolution: - { - integrity: sha512-3+F6kI17QtcgKQD9NKlLZ4LUy0koXULzkX1FgyILU17PptClnGOu+c+jT+PlZK2GsCjucLwQLjOQQkRIczU3uA==, - } - hasBin: true + '@nuxt/telemetry@1.5.0': dependencies: arg: 5.0.2 chalk: 4.1.2 ci-info: 3.8.0 - consola: 2.15.3 + consola: 3.2.3 create-require: 1.1.1 - defu: 6.1.2 - destr: 1.2.2 + defu: 6.1.4 + destr: 2.0.3 dotenv: 9.0.2 fs-extra: 8.1.0 - git-url-parse: 13.1.0 + git-url-parse: 13.1.1 inquirer: 7.3.3 - jiti: 1.20.0 - nanoid: 3.3.6 + jiti: 1.21.0 + nanoid: 3.3.8 node-fetch: 2.7.0 parse-git-config: 3.0.0 rc9: 2.1.1 - std-env: 3.4.3 + std-env: 3.7.0 transitivePeerDependencies: - encoding - dev: false - - /@nuxt/types@2.17.0: - resolution: - { - integrity: sha512-McgjOTBpnbicjo830MYiCtI1zzkkq0W23GlxcWfLAsi5vjqjfcBn9w9emVhOCDfNNbhMPsvfp3B5MyPZEaqo9A==, - } - engines: { node: ^14.18.0 || >=16.10.0 } - dependencies: - '@types/babel__core': 7.20.1 - '@types/compression': 1.7.2 - '@types/connect': 3.4.35 - '@types/etag': 1.8.1 - '@types/file-loader': 5.0.1 - '@types/html-minifier': 4.0.2 - '@types/less': 3.0.3 + + '@nuxt/types@2.18.1': + dependencies: + '@types/babel__core': 7.20.5 + '@types/compression': 1.7.5 + '@types/connect': 3.4.38 + '@types/etag': 1.8.3 + '@types/file-loader': 5.0.4 + '@types/html-minifier-terser': 7.0.2 + '@types/less': 3.0.6 '@types/node': 16.18.55 - '@types/optimize-css-assets-webpack-plugin': 5.0.5 - '@types/pug': 2.0.6 - '@types/serve-static': 1.15.1 + '@types/optimize-css-assets-webpack-plugin': 5.0.8 + '@types/pug': 2.0.10 + '@types/serve-static': 1.15.7 '@types/terser-webpack-plugin': 4.2.1 - '@types/webpack': 4.41.33 + '@types/webpack': 4.41.38 '@types/webpack-bundle-analyzer': 3.9.5 '@types/webpack-hot-middleware': 2.25.5 - dev: true - /@nuxt/typescript-build@2.1.0(@nuxt/types@2.17.0)(eslint@8.53.0)(vue-template-compiler@2.7.15)(webpack@5.87.0): - resolution: - { - integrity: sha512-7TLMpfzgOckf3cBkzoPFns6Xl8FzY6MoFfm/5HUE47QeTWAdOG9ZFxMrVhHWieZHYUuV+k6byRtaRv4S/3R8zA==, - } - peerDependencies: - '@nuxt/types': '>=2.13.1' + '@nuxt/typescript-build@3.0.2(@nuxt/types@2.18.1)(eslint@8.57.1)(typescript@4.9.5)(vue-template-compiler@2.7.16)(webpack@5.104.1)': dependencies: - '@nuxt/types': 2.17.0 - consola: 2.15.3 - fork-ts-checker-webpack-plugin: 6.5.3(eslint@8.53.0)(typescript@4.2.4)(vue-template-compiler@2.7.15)(webpack@5.87.0) - ts-loader: 8.4.0(typescript@4.2.4)(webpack@5.87.0) - typescript: 4.2.4 + '@nuxt/types': 2.18.1 + consola: 3.2.3 + defu: 6.1.2 + fork-ts-checker-webpack-plugin: 6.5.3(eslint@8.57.1)(typescript@4.9.5)(vue-template-compiler@2.7.16)(webpack@5.104.1) + ts-loader: 8.4.0(typescript@4.9.5)(webpack@5.104.1) + typescript: 4.9.5 transitivePeerDependencies: - eslint - vue-template-compiler - webpack - dev: true - - /@nuxt/ui-templates@1.3.1: - resolution: - { - integrity: sha512-5gc02Pu1HycOVUWJ8aYsWeeXcSTPe8iX8+KIrhyEtEoOSkY0eMBuo0ssljB8wALuEmepv31DlYe5gpiRwkjESA==, - } - dev: true - - /@nuxt/utils@2.17.2: - resolution: - { - integrity: sha512-vb0U/+I5omMQK6Nb3QlWYeStRhWeGJeR3tEGxc+OZw41T1OgqfRlg32tNBkMDqlNpSRjTKyWjIYGHBoNNrB2SA==, - } - engines: { node: ^14.18.0 || >=16.10.0 } + + '@nuxt/ui-templates@1.3.1': {} + + '@nuxt/utils@2.18.1': dependencies: consola: 3.2.3 create-require: 1.1.1 - fs-extra: 10.1.0 + fs-extra: 11.2.0 hash-sum: 2.0.0 - jiti: 1.20.0 + jiti: 1.21.6 lodash: 4.17.21 proper-lockfile: 4.1.2 - semver: 7.5.4 - serialize-javascript: 6.0.1 + semver: 7.7.3 + serialize-javascript: 6.0.2 signal-exit: 4.1.0 - ua-parser-js: 1.0.36 - ufo: 1.3.1 - dev: false - - /@nuxt/vue-app@2.17.2: - resolution: - { - integrity: sha512-5bgothZDKBG9p1DKTvI74DfJDk+3fdRbjT6JMmiciyV55IMm7bE6eGVpcnUJ8bGjuKWSOiJbjaGG4qZ18EMd8Q==, - } - engines: { node: ^14.18.0 || >=16.10.0 } - dependencies: - node-fetch-native: 1.4.1 - ufo: 1.3.1 + ua-parser-js: 1.0.38 + ufo: 1.6.1 + + '@nuxt/vue-app@2.18.1': + dependencies: + node-fetch-native: 1.6.7 + ufo: 1.6.1 unfetch: 5.0.0 - vue: 2.7.15 + vue: 2.7.16 vue-client-only: 2.1.0 vue-meta: 2.4.0 vue-no-ssr: 1.1.1 - vue-router: 3.6.5(vue@2.7.15) - vue-template-compiler: 2.7.15 - vuex: 3.6.2(vue@2.7.15) - dev: false - - /@nuxt/vue-renderer@2.17.2: - resolution: - { - integrity: sha512-08OaxLDwIqR/Wh0+CYhoMJTXz9C9hV5dBE1d4Pw39nUwWRe+eQRcFxrpeWyXMV//cvS6mwlBSYsAK3fjMF3uNA==, - } - engines: { node: ^14.18.0 || >=16.10.0 } + vue-router: 3.6.5(vue@2.7.16) + vue-template-compiler: 2.7.16 + vuex: 3.6.2(vue@2.7.16) + + '@nuxt/vue-renderer@2.18.1': dependencies: '@nuxt/devalue': 2.0.2 - '@nuxt/utils': 2.17.2 + '@nuxt/utils': 2.18.1 consola: 3.2.3 - defu: 6.1.2 - fs-extra: 10.1.0 + defu: 6.1.4 + fs-extra: 11.2.0 lodash: 4.17.21 lru-cache: 5.1.1 - ufo: 1.3.1 - vue: 2.7.15 + ufo: 1.6.1 + vue: 2.7.16 vue-meta: 2.4.0 - vue-server-renderer: 2.7.15 - dev: false - - /@nuxt/webpack@2.17.2(babel-core@7.0.0-bridge.0)(prettier@3.0.3)(typescript@4.9.5)(vue@2.7.15): - resolution: - { - integrity: sha512-QcV5Oo0ObpAG8bAiqYbIIf+SwhVJutTSOgf05NHob9VcPEXwCwDjO8zICrVgKvdjWnmlg5GFySaVMa407puE6g==, - } - engines: { node: ^14.18.0 || >=16.10.0 } - dependencies: - '@babel/core': 7.23.2 - '@nuxt/babel-preset-app': 2.17.2(vue@2.7.15) - '@nuxt/friendly-errors-webpack-plugin': 2.5.2(webpack@4.47.0) - '@nuxt/utils': 2.17.2 - babel-loader: 8.3.0(@babel/core@7.23.2)(webpack@4.47.0) + vue-server-renderer: 2.7.16 + + '@nuxt/webpack@2.18.1(babel-core@7.0.0-bridge.0(@babel/core@7.28.4))(ejs@3.1.10)(handlebars@4.7.8)(prettier@3.8.1)(typescript@4.9.5)(vue@2.7.16)': + dependencies: + '@babel/core': 7.24.7 + '@nuxt/babel-preset-app': 2.18.1(vue@2.7.16) + '@nuxt/friendly-errors-webpack-plugin': 2.6.0(webpack@4.47.0) + '@nuxt/utils': 2.18.1 + babel-loader: 8.3.0(@babel/core@7.24.7)(webpack@4.47.0) cache-loader: 4.1.0(webpack@4.47.0) - caniuse-lite: 1.0.30001561 + caniuse-lite: 1.0.30001639 consola: 3.2.3 css-loader: 5.2.7(webpack@4.47.0) - cssnano: 6.0.1(postcss@8.4.31) + cssnano: 7.0.3(postcss@8.5.6) eventsource-polyfill: 0.9.6 extract-css-chunks-webpack-plugin: 4.10.0(webpack@4.47.0) file-loader: 6.2.0(webpack@4.47.0) @@ -4912,35 +12036,36 @@ packages: hash-sum: 2.0.0 html-webpack-plugin: 4.5.2(webpack@4.47.0) lodash: 4.17.21 - memory-fs: 0.5.0 + memfs: 4.9.3 + mkdirp: 0.5.6 optimize-css-assets-webpack-plugin: 6.0.1(webpack@4.47.0) pify: 5.0.0 pnp-webpack-plugin: 1.7.0(typescript@4.9.5) - postcss: 8.4.31 - postcss-import: 15.1.0(postcss@8.4.31) + postcss: 8.5.6 + postcss-import: 15.1.0(postcss@8.5.6) postcss-import-resolver: 2.0.0 - postcss-loader: 4.3.0(postcss@8.4.31)(webpack@4.47.0) - postcss-preset-env: 9.3.0(postcss@8.4.31) - postcss-url: 10.1.3(postcss@8.4.31) - semver: 7.5.4 - std-env: 3.4.3 + postcss-loader: 4.3.0(postcss@8.5.6)(webpack@4.47.0) + postcss-preset-env: 9.5.15(postcss@8.5.6) + postcss-url: 10.1.3(postcss@8.5.6) + semver: 7.7.3 + std-env: 3.7.0 style-resources-loader: 1.5.0(webpack@4.47.0) terser-webpack-plugin: 4.2.3(webpack@4.47.0) thread-loader: 3.0.4(webpack@4.47.0) time-fix-plugin: 2.0.7(webpack@4.47.0) - ufo: 1.3.1 + ufo: 1.6.1 upath: 2.0.1 - url-loader: 4.1.1(file-loader@6.2.0)(webpack@4.47.0) - vue-loader: 15.11.1(babel-core@7.0.0-bridge.0)(cache-loader@4.1.0)(css-loader@5.2.7)(lodash@4.17.21)(prettier@3.0.3)(vue-template-compiler@2.7.15)(webpack@4.47.0) + url-loader: 4.1.1(file-loader@6.2.0(webpack@5.104.1))(webpack@4.47.0) + vue-loader: 15.11.1(babel-core@7.0.0-bridge.0(@babel/core@7.28.4))(cache-loader@4.1.0(webpack@4.47.0))(css-loader@5.2.7(webpack@5.104.1))(ejs@3.1.10)(handlebars@4.7.8)(lodash@4.17.21)(prettier@3.8.1)(vue-template-compiler@2.7.16)(webpack@4.47.0) vue-style-loader: 4.1.3 - vue-template-compiler: 2.7.15 - watchpack: 2.4.0 + vue-template-compiler: 2.7.16 + watchpack: 2.5.0 webpack: 4.47.0 - webpack-bundle-analyzer: 4.9.1 - webpack-dev-middleware: 5.3.3(webpack@4.47.0) - webpack-hot-middleware: 2.25.4 + webpack-bundle-analyzer: 4.10.2 + webpack-dev-middleware: 5.3.4(webpack@4.47.0) + webpack-hot-middleware: 2.26.1 webpack-node-externals: 3.0.0 - webpackbar: 5.0.2(webpack@4.47.0) + webpackbar: 6.0.1(webpack@4.47.0) transitivePeerDependencies: - '@vue/compiler-sfc' - arc-templates @@ -5004,111 +12129,71 @@ packages: - webpack-cli - webpack-command - whiskers - dev: false - /@nuxtjs/dotenv@1.4.1: - resolution: - { - integrity: sha512-DpdObsvRwC8d89I9mzz6pBg6e/PEXHazDM57DOI1mmML2ZjHfQ/DvkjlSzUL7T+TnW3b/a4Ks5wQx08DqFBmeQ==, - } + '@nuxtjs/dotenv@1.4.2': dependencies: - consola: 2.15.3 + consola: 3.2.3 dotenv: 8.6.0 - dev: false - /@nuxtjs/eslint-config-typescript@12.1.0(eslint@8.53.0)(typescript@4.9.5): - resolution: - { - integrity: sha512-l2fLouDYwdAvCZEEw7wGxOBj+i8TQcHFu3zMPTLqKuv1qu6WcZIr0uztkbaa8ND1uKZ9YPqKx6UlSOjM4Le69Q==, - } - peerDependencies: - eslint: ^8.48.0 + '@nuxtjs/eslint-config-typescript@12.1.0(eslint@8.57.1)(typescript@4.9.5)': dependencies: - '@nuxtjs/eslint-config': 12.0.0(@typescript-eslint/parser@6.7.3)(eslint-import-resolver-typescript@3.6.1)(eslint@8.53.0) - '@typescript-eslint/eslint-plugin': 6.7.3(@typescript-eslint/parser@6.7.3)(eslint@8.53.0)(typescript@4.9.5) - '@typescript-eslint/parser': 6.7.3(eslint@8.53.0)(typescript@4.9.5) - eslint: 8.53.0 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.7.3)(eslint-plugin-import@2.28.1)(eslint@8.53.0) - eslint-plugin-import: 2.28.1(@typescript-eslint/parser@6.7.3)(eslint-import-resolver-typescript@3.6.1)(eslint@8.53.0) - eslint-plugin-vue: 9.17.0(eslint@8.53.0) + '@nuxtjs/eslint-config': 12.0.0(@typescript-eslint/parser@6.7.3(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) + '@typescript-eslint/eslint-plugin': 6.7.3(@typescript-eslint/parser@6.7.3(eslint@8.57.1)(typescript@4.9.5))(eslint@8.57.1)(typescript@4.9.5) + '@typescript-eslint/parser': 6.7.3(eslint@8.57.1)(typescript@4.9.5) + eslint: 8.57.1 + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.7.3(eslint@8.57.1)(typescript@4.9.5))(eslint-plugin-import@2.28.1)(eslint@8.57.1) + eslint-plugin-import: 2.28.1(@typescript-eslint/parser@6.7.3(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) + eslint-plugin-vue: 9.33.0(eslint@8.57.1) transitivePeerDependencies: - eslint-import-resolver-node - eslint-import-resolver-webpack - supports-color - typescript - dev: true - /@nuxtjs/eslint-config@12.0.0(@typescript-eslint/parser@6.7.3)(eslint-import-resolver-typescript@3.6.1)(eslint@8.53.0): - resolution: - { - integrity: sha512-ewenelo75x0eYEUK+9EBXjc/OopQCvdkmYmlZuoHq5kub/vtiRpyZ/autppwokpHUq8tiVyl2ejMakoiHiDTrg==, - } - peerDependencies: - eslint: ^8.23.0 + '@nuxtjs/eslint-config@12.0.0(@typescript-eslint/parser@6.7.3(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1)': dependencies: - eslint: 8.53.0 - eslint-config-standard: 17.1.0(eslint-plugin-import@2.28.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.53.0) - eslint-plugin-import: 2.28.1(@typescript-eslint/parser@6.7.3)(eslint-import-resolver-typescript@3.6.1)(eslint@8.53.0) - eslint-plugin-n: 15.7.0(eslint@8.53.0) - eslint-plugin-node: 11.1.0(eslint@8.53.0) - eslint-plugin-promise: 6.1.1(eslint@8.53.0) - eslint-plugin-unicorn: 44.0.2(eslint@8.53.0) - eslint-plugin-vue: 9.16.1(eslint@8.53.0) + eslint: 8.57.1 + eslint-config-standard: 17.1.0(eslint-plugin-import@2.28.1)(eslint-plugin-n@15.7.0(eslint@8.57.1))(eslint-plugin-promise@6.1.1(eslint@8.57.1))(eslint@8.57.1) + eslint-plugin-import: 2.28.1(@typescript-eslint/parser@6.7.3(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) + eslint-plugin-n: 15.7.0(eslint@8.57.1) + eslint-plugin-node: 11.1.0(eslint@8.57.1) + eslint-plugin-promise: 6.1.1(eslint@8.57.1) + eslint-plugin-unicorn: 44.0.2(eslint@8.57.1) + eslint-plugin-vue: 9.33.0(eslint@8.57.1) local-pkg: 0.4.3 transitivePeerDependencies: - '@typescript-eslint/parser' - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - dev: true - /@nuxtjs/eslint-module@4.1.0(eslint@8.53.0)(vite@4.4.9)(webpack@5.87.0): - resolution: - { - integrity: sha512-lW9ozEjOrnU8Uot3GOAZ/0ThNAds0d6UAp9n46TNxcTvH/MOcAggGbMNs16c0HYT2HlyPQvXORCHQ5+9p87mmw==, - } - peerDependencies: - eslint: '>=7' + '@nuxtjs/eslint-module@4.1.0(eslint@8.57.1)(rollup@3.30.0)(vite@4.5.3(@types/node@25.1.0)(sass@1.32.13)(terser@5.44.1))(webpack@5.104.1)': dependencies: - '@nuxt/kit': 3.7.4 + '@nuxt/kit': 3.7.4(rollup@3.30.0) chokidar: 3.5.3 - eslint: 8.53.0 - eslint-webpack-plugin: 4.0.1(eslint@8.53.0)(webpack@5.87.0) + eslint: 8.57.1 + eslint-webpack-plugin: 4.0.1(eslint@8.57.1)(webpack@5.104.1) pathe: 1.1.1 - vite-plugin-eslint: 1.8.1(eslint@8.53.0)(vite@4.4.9) + vite-plugin-eslint: 1.8.1(eslint@8.57.1)(vite@4.5.3(@types/node@25.1.0)(sass@1.32.13)(terser@5.44.1)) transitivePeerDependencies: - rollup - supports-color - vite - webpack - dev: true - /@nuxtjs/firebase@8.2.2(@firebase/app-types@0.9.0)(firebase@9.23.0)(nuxt@2.17.2): - resolution: - { - integrity: sha512-j+kW0utwq23w71D0I4RyOc9/eYGe8WpsoI2GD9PT744rMWmj4MFHASjmgyDPk2KdZGxsknxUW6yq29aLd0E2ow==, - } - peerDependencies: - firebase: ^9.6.2 - nuxt: ^2.15.6 + '@nuxtjs/firebase@8.2.2(@firebase/app-types@0.9.2)(firebase@10.14.1)(nuxt@2.18.1(babel-core@7.0.0-bridge.0(@babel/core@7.28.4))(consola@3.2.3)(ejs@3.1.10)(handlebars@4.7.8)(prettier@3.8.1)(typescript@4.9.5)(vue@2.7.16))': dependencies: consola: 2.15.3 - firebase: 9.23.0 - nuxt: 2.17.2(babel-core@7.0.0-bridge.0)(consola@3.2.3)(prettier@3.0.3)(typescript@4.9.5)(vue@2.7.15) + firebase: 10.14.1 + nuxt: 2.18.1(babel-core@7.0.0-bridge.0(@babel/core@7.28.4))(consola@3.2.3)(ejs@3.1.10)(handlebars@4.7.8)(prettier@3.8.1)(typescript@4.9.5)(vue@2.7.16) optionalDependencies: - firebase-admin: 10.3.0(@firebase/app-types@0.9.0) + firebase-admin: 10.3.0(@firebase/app-types@0.9.2) transitivePeerDependencies: - '@firebase/app-types' - encoding - supports-color - dev: false - /@nuxtjs/sitemap@2.4.0: - resolution: - { - integrity: sha512-TVgIYOtPp7KAfaUo76WRpGbO20j4D/xi/A7shFIGjARHs+FvfAWXNCtBT87dTwe/RoYzAsEKtijFFUTaSu5bUA==, - } - engines: { node: '>=8.9.0', npm: '>=5.0.0' } + '@nuxtjs/sitemap@2.4.0': dependencies: async-cache: 1.1.0 consola: 2.15.3 @@ -5119,42 +12204,31 @@ packages: lodash.unionby: 4.8.0 minimatch: 3.1.2 sitemap: 4.1.1 - dev: false - /@nuxtjs/stylelint-module@5.1.0(postcss@8.4.31)(stylelint@15.11.0)(vite@4.4.9)(webpack@5.87.0): - resolution: - { - integrity: sha512-zpWn5nJfFstBh7PUBjsBNu6rFCN2wtz3JyLGuEqCh4yo7orKclDM3S931E3kMD6Tszpvobhq1CMsgbcZL6K3aA==, - } - peerDependencies: - stylelint: '>=13' + '@nuxtjs/stylelint-module@5.2.0(postcss@8.5.6)(rollup@3.30.0)(stylelint@15.11.0(typescript@4.9.5))(vite@4.5.3(@types/node@25.1.0)(sass@1.32.13)(terser@5.44.1))(webpack@5.104.1)': dependencies: - '@nuxt/kit': 3.7.4 - chokidar: 3.5.3 - pathe: 1.1.1 + '@nuxt/kit': 3.12.2(rollup@3.30.0) + chokidar: 3.6.0 + pathe: 1.1.2 stylelint: 15.11.0(typescript@4.9.5) - stylelint-webpack-plugin: 4.1.1(stylelint@15.11.0)(webpack@5.87.0) - vite-plugin-stylelint: 4.3.0(postcss@8.4.31)(stylelint@15.11.0)(vite@4.4.9) + stylelint-webpack-plugin: 5.0.1(stylelint@15.11.0(typescript@4.9.5))(webpack@5.104.1) + vite-plugin-stylelint: 5.3.1(postcss@8.5.6)(rollup@3.30.0)(stylelint@15.11.0(typescript@4.9.5))(vite@4.5.3(@types/node@25.1.0)(sass@1.32.13)(terser@5.44.1)) transitivePeerDependencies: - '@types/stylelint' + - magicast - postcss - rollup - supports-color - vite - webpack - dev: true - /@nuxtjs/vuetify@1.12.3(vue@2.7.15)(webpack@5.87.0): - resolution: - { - integrity: sha512-6uVL3cfESMB00eVjJTNkyU4jvuPTGPn1yteo7lQTH6v+fxHcPaOgvzVYHIKSHIz1DecuOiB5c9b+YjsRP5+C8A==, - } + '@nuxtjs/vuetify@1.12.3(vue@2.7.16)(webpack@5.104.1)': dependencies: deepmerge: 4.3.1 sass: 1.32.13 - sass-loader: 10.4.1(sass@1.32.13)(webpack@5.87.0) - vuetify: 2.7.1(vue@2.7.15) - vuetify-loader: 1.9.2(vue@2.7.15)(vuetify@2.7.1)(webpack@5.87.0) + sass-loader: 10.4.1(sass@1.32.13)(webpack@5.104.1) + vuetify: 2.7.2(vue@2.7.16) + vuetify-loader: 1.9.2(vue@2.7.16)(vuetify@2.7.2(vue@2.7.16))(webpack@5.104.1) transitivePeerDependencies: - fibers - gm @@ -5163,686 +12237,290 @@ packages: - sharp - vue - webpack - dev: true - /@nuxtjs/youch@4.2.3: - resolution: - { - integrity: sha512-XiTWdadTwtmL/IGkNqbVe+dOlT+IMvcBu7TvKI7plWhVQeBCQ9iKhk3jgvVWFyiwL2yHJDlEwOM5v9oVES5Xmw==, - } + '@nuxtjs/youch@4.2.3': dependencies: cookie: 0.3.1 mustache: 2.3.2 stack-trace: 0.0.10 - dev: false - - /@one-ini/wasm@0.1.1: - resolution: - { - integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==, - } - dev: true - - /@panva/asn1.js@1.0.0: - resolution: - { - integrity: sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==, - } - engines: { node: '>=10.13.0' } - requiresBuild: true - dev: false + + '@one-ini/wasm@0.1.1': {} + + '@panva/asn1.js@1.0.0': + optional: true + + '@pkgjs/parseargs@0.11.0': optional: true - /@polka/url@1.0.0-next.23: - resolution: - { - integrity: sha512-C16M+IYz0rgRhWZdCmK+h58JMv8vijAA61gmz2rspCSwKwzBebpdcsiUmwrtJRdphuY30i6BSLEOP8ppbNLyLg==, - } - dev: false - - /@protobufjs/aspromise@1.1.2: - resolution: - { - integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==, - } - dev: false - - /@protobufjs/base64@1.1.2: - resolution: - { - integrity: sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==, - } - dev: false - - /@protobufjs/codegen@2.0.4: - resolution: - { - integrity: sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==, - } - dev: false - - /@protobufjs/eventemitter@1.1.0: - resolution: - { - integrity: sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==, - } - dev: false - - /@protobufjs/fetch@1.1.0: - resolution: - { - integrity: sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==, - } + '@pkgr/core@0.2.9': {} + + '@polka/url@1.0.0-next.23': {} + + '@protobufjs/aspromise@1.1.2': {} + + '@protobufjs/base64@1.1.2': {} + + '@protobufjs/codegen@2.0.4': {} + + '@protobufjs/eventemitter@1.1.0': {} + + '@protobufjs/fetch@1.1.0': dependencies: '@protobufjs/aspromise': 1.1.2 '@protobufjs/inquire': 1.1.0 - dev: false - - /@protobufjs/float@1.0.2: - resolution: - { - integrity: sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==, - } - dev: false - - /@protobufjs/inquire@1.1.0: - resolution: - { - integrity: sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==, - } - dev: false - - /@protobufjs/path@1.1.2: - resolution: - { - integrity: sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==, - } - dev: false - - /@protobufjs/pool@1.1.0: - resolution: - { - integrity: sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==, - } - dev: false - - /@protobufjs/utf8@1.1.0: - resolution: - { - integrity: sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==, - } - dev: false - - /@rollup/pluginutils@4.2.1: - resolution: - { - integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==, - } - engines: { node: '>= 8.0.0' } + + '@protobufjs/float@1.0.2': {} + + '@protobufjs/inquire@1.1.0': {} + + '@protobufjs/path@1.1.2': {} + + '@protobufjs/pool@1.1.0': {} + + '@protobufjs/utf8@1.1.0': {} + + '@rollup/pluginutils@4.2.1': dependencies: estree-walker: 2.0.2 picomatch: 2.3.1 - dev: true - /@rollup/pluginutils@5.0.4: - resolution: - { - integrity: sha512-0KJnIoRI8A+a1dqOYLxH8vBf8bphDmty5QvIm2hqm7oFCFYKCAZWWd2hXgMibaPsNDhI0AtpYfQZJG47pt/k4g==, - } - engines: { node: '>=14.0.0' } - peerDependencies: - rollup: ^1.20.0||^2.0.0||^3.0.0 - peerDependenciesMeta: - rollup: - optional: true + '@rollup/pluginutils@5.0.4(rollup@3.30.0)': + dependencies: + '@types/estree': 1.0.8 + estree-walker: 2.0.2 + picomatch: 2.3.1 + optionalDependencies: + rollup: 3.30.0 + + '@rollup/pluginutils@5.1.0(rollup@3.30.0)': dependencies: - '@types/estree': 1.0.2 + '@types/estree': 1.0.8 estree-walker: 2.0.2 picomatch: 2.3.1 - dev: true + optionalDependencies: + rollup: 3.30.0 - /@sinclair/typebox@0.27.8: - resolution: - { - integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==, - } - dev: true + '@sinclair/typebox@0.27.8': {} - /@sinonjs/commons@1.8.6: - resolution: - { - integrity: sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==, - } + '@sinclair/typebox@0.34.41': {} + + '@sindresorhus/merge-streams@2.3.0': {} + + '@sinonjs/commons@3.0.1': dependencies: type-detect: 4.0.8 - dev: true - - /@sinonjs/fake-timers@8.1.0: - resolution: - { - integrity: sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==, - } - dependencies: - '@sinonjs/commons': 1.8.6 - dev: true - - /@tootallnate/once@1.1.2: - resolution: - { - integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==, - } - engines: { node: '>= 6' } - dev: true - - /@tootallnate/once@2.0.0: - resolution: - { - integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==, - } - engines: { node: '>= 10' } - requiresBuild: true - dev: false + + '@sinonjs/fake-timers@13.0.5': + dependencies: + '@sinonjs/commons': 3.0.1 + + '@tootallnate/once@2.0.0': optional: true - /@trysound/sax@0.2.0: - resolution: - { - integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==, - } - engines: { node: '>=10.13.0' } - dev: false - - /@tsconfig/node10@1.0.9: - resolution: - { - integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==, - } - dev: true - - /@tsconfig/node12@1.0.11: - resolution: - { - integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==, - } - dev: true - - /@tsconfig/node14@1.0.3: - resolution: - { - integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==, - } - dev: true - - /@tsconfig/node16@1.0.4: - resolution: - { - integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==, - } - dev: true - - /@types/babel__core@7.20.1: - resolution: - { - integrity: sha512-aACu/U/omhdk15O4Nfb+fHgH/z3QsfQzpnvRZhYhThms83ZnAOZz7zZAWO7mn2yyNQaA4xTO8GLK3uqFU4bYYw==, - } - dependencies: - '@babel/parser': 7.23.0 - '@babel/types': 7.23.0 - '@types/babel__generator': 7.6.5 - '@types/babel__template': 7.4.2 - '@types/babel__traverse': 7.20.2 - dev: true - - /@types/babel__core@7.20.2: - resolution: - { - integrity: sha512-pNpr1T1xLUc2l3xJKuPtsEky3ybxN3m4fJkknfIpTCTfIZCDW57oAg+EfCgIIp2rvCe0Wn++/FfodDS4YXxBwA==, - } - dependencies: - '@babel/parser': 7.23.0 - '@babel/types': 7.23.0 - '@types/babel__generator': 7.6.5 - '@types/babel__template': 7.4.2 - '@types/babel__traverse': 7.20.2 - dev: true - - /@types/babel__generator@7.6.5: - resolution: - { - integrity: sha512-h9yIuWbJKdOPLJTbmSpPzkF67e659PbQDba7ifWm5BJ8xTv+sDmS7rFmywkWOvXedGTivCdeGSIIX8WLcRTz8w==, - } - dependencies: - '@babel/types': 7.23.0 - dev: true - - /@types/babel__template@7.4.2: - resolution: - { - integrity: sha512-/AVzPICMhMOMYoSx9MoKpGDKdBRsIXMNByh1PXSZoa+v6ZoLa8xxtsT/uLQ/NJm0XVAWl/BvId4MlDeXJaeIZQ==, - } - dependencies: - '@babel/parser': 7.23.0 - '@babel/types': 7.23.0 - dev: true - - /@types/babel__traverse@7.20.2: - resolution: - { - integrity: sha512-ojlGK1Hsfce93J0+kn3H5R73elidKUaZonirN33GSmgTUMpzI/MIFfSpF3haANe3G1bEBS9/9/QEqwTzwqFsKw==, - } - dependencies: - '@babel/types': 7.23.0 - dev: true - - /@types/body-parser@1.19.3: - resolution: - { - integrity: sha512-oyl4jvAfTGX9Bt6Or4H9ni1Z447/tQuxnZsytsCaExKlmJiU8sFgnIBRzJUpKwB5eWn9HuBYlUlVA74q/yN0eQ==, - } - dependencies: - '@types/connect': 3.4.35 - '@types/node': 20.8.0 - - /@types/clean-css@4.2.7: - resolution: - { - integrity: sha512-lcoZHjUAANLTACLGi+O/0pN+oKQAQ8zAMWJSxiBRNLxqZG/WE8hfXJUs1eYwJOvOnDJrvxU1kR77UiVJ3+9N0Q==, - } - dependencies: - '@types/node': 20.8.0 - source-map: 0.6.1 - dev: true + '@trysound/sax@0.2.0': {} + + '@tybys/wasm-util@0.10.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.28.0 + '@babel/types': 7.28.2 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.28.2 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.28.0 + '@babel/types': 7.28.2 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.28.2 + + '@types/body-parser@1.19.3': + dependencies: + '@types/connect': 3.4.38 + '@types/node': 25.1.0 - /@types/compression@1.7.2: - resolution: - { - integrity: sha512-lwEL4M/uAGWngWFLSG87ZDr2kLrbuR8p7X+QZB1OQlT+qkHsCPDVFnHPyXf4Vyl4yDDorNY+mAhosxkCvppatg==, - } + '@types/compression@1.7.5': dependencies: '@types/express': 4.17.18 - dev: true - /@types/connect@3.4.35: - resolution: - { - integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==, - } + '@types/connect@3.4.38': dependencies: '@types/node': 16.18.55 - /@types/eslint-scope@3.7.5: - resolution: - { - integrity: sha512-JNvhIEyxVW6EoMIFIvj93ZOywYFatlpu9deeH6eSx6PE3WHYvHaQtmHmQeNw7aA81bYGBPPQqdtBm6b1SsQMmA==, - } + '@types/eslint-scope@3.7.7': dependencies: - '@types/eslint': 8.44.3 - '@types/estree': 1.0.2 + '@types/eslint': 9.6.1 + '@types/estree': 1.0.8 - /@types/eslint@8.44.3: - resolution: - { - integrity: sha512-iM/WfkwAhwmPff3wZuPLYiHX18HI24jU8k1ZSH7P8FHwxTjZ2P6CoX2wnF43oprR+YXJM6UUxATkNvyv/JHd+g==, - } + '@types/eslint@8.44.3': dependencies: - '@types/estree': 1.0.2 - '@types/json-schema': 7.0.13 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 - /@types/estree@1.0.2: - resolution: - { - integrity: sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==, - } + '@types/eslint@9.6.1': + dependencies: + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + + '@types/estree@1.0.8': {} - /@types/etag@1.8.1: - resolution: - { - integrity: sha512-bsKkeSqN7HYyYntFRAmzcwx/dKW4Wa+KVMTInANlI72PWLQmOpZu96j0OqHZGArW4VQwCmJPteQlXaUDeOB0WQ==, - } + '@types/etag@1.8.3': dependencies: '@types/node': 16.18.55 - dev: true - /@types/express-serve-static-core@4.17.37: - resolution: - { - integrity: sha512-ZohaCYTgGFcOP7u6aJOhY9uIZQgZ2vxC2yWoArY+FeDXlqeH66ZVBjgvg+RLVAS/DWNq4Ap9ZXu1+SUQiiWYMg==, - } + '@types/express-serve-static-core@4.17.37': dependencies: - '@types/node': 20.8.0 + '@types/node': 25.1.0 '@types/qs': 6.9.8 '@types/range-parser': 1.2.5 '@types/send': 0.17.2 - /@types/express@4.17.18: - resolution: - { - integrity: sha512-Sxv8BSLLgsBYmcnGdGjjEjqET2U+AKAdCRODmMiq02FgjwuV75Ut85DRpvFjyw/Mk0vgUOliGRU0UUmuuZHByQ==, - } + '@types/express@4.17.18': dependencies: '@types/body-parser': 1.19.3 '@types/express-serve-static-core': 4.17.37 '@types/qs': 6.9.8 - '@types/serve-static': 1.15.1 - - /@types/file-loader@5.0.1: - resolution: - { - integrity: sha512-FHPPuRb/Ts/25qvNU/mQGwRZUp793nBxYqXd/KwApykxATagqrO4+2EEcGDm/DuXyV/EkOa04umS1DQ8tQSomg==, - } - dependencies: - '@types/webpack': 4.41.33 - dev: true - - /@types/graceful-fs@4.1.7: - resolution: - { - integrity: sha512-MhzcwU8aUygZroVwL2jeYk6JisJrPl/oov/gsgGCue9mkgl9wjGbzReYQClxiUgFDnib9FuHqTndccKeZKxTRw==, - } - dependencies: - '@types/node': 20.8.0 - dev: true - - /@types/html-minifier-terser@5.1.2: - resolution: - { - integrity: sha512-h4lTMgMJctJybDp8CQrxTUiiYmedihHWkjnF/8Pxseu2S6Nlfcy8kwboQ8yejh456rP2yWoEVm1sS/FVsfM48w==, - } - dev: false - - /@types/html-minifier@4.0.2: - resolution: - { - integrity: sha512-4IkmkXJP/25R2fZsCHDX2abztXuQRzUAZq39PfCMz2loLFj8vS9y7aF6vDl58koXSTpsF+eL4Lc5Y4Aww/GCTQ==, - } - dependencies: - '@types/clean-css': 4.2.7 - '@types/relateurl': 0.2.30 - '@types/uglify-js': 3.17.2 - dev: true - - /@types/istanbul-lib-coverage@2.0.4: - resolution: - { - integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==, - } - dev: true - - /@types/istanbul-lib-report@3.0.1: - resolution: - { - integrity: sha512-gPQuzaPR5h/djlAv2apEG1HVOyj1IUs7GpfMZixU0/0KXT3pm64ylHuMUI1/Akh+sq/iikxg6Z2j+fcMDXaaTQ==, - } - dependencies: - '@types/istanbul-lib-coverage': 2.0.4 - dev: true - - /@types/istanbul-reports@3.0.2: - resolution: - { - integrity: sha512-kv43F9eb3Lhj+lr/Hn6OcLCs/sSM8bt+fIaP11rCYngfV6NVjzWXJ17owQtDQTL9tQ8WSLUrGsSJ6rJz0F1w1A==, - } - dependencies: - '@types/istanbul-lib-report': 3.0.1 - dev: true - - /@types/json-schema@7.0.13: - resolution: - { - integrity: sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==, - } - - /@types/json5@0.0.29: - resolution: - { - integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==, - } - dev: true - - /@types/jsonwebtoken@8.5.9: - resolution: - { - integrity: sha512-272FMnFGzAVMGtu9tkr29hRL6bZj4Zs1KZNeHLnKqAvp06tAIcarTMwOh8/8bz4FmKRcMxZhZNeUAQsNLoiPhg==, - } - requiresBuild: true - dependencies: - '@types/node': 20.8.0 - dev: false + '@types/serve-static': 1.15.7 + + '@types/file-loader@5.0.4': + dependencies: + '@types/webpack': 4.41.38 + + '@types/html-minifier-terser@5.1.2': {} + + '@types/html-minifier-terser@7.0.2': {} + + '@types/http-errors@2.0.4': {} + + '@types/istanbul-lib-coverage@2.0.6': {} + + '@types/istanbul-lib-report@3.0.3': + dependencies: + '@types/istanbul-lib-coverage': 2.0.6 + + '@types/istanbul-reports@3.0.4': + dependencies: + '@types/istanbul-lib-report': 3.0.3 + + '@types/jsdom@21.1.7': + dependencies: + '@types/node': 25.1.0 + '@types/tough-cookie': 4.0.5 + parse5: 7.3.0 + + '@types/json-schema@7.0.15': {} + + '@types/json5@0.0.29': {} + + '@types/jsonwebtoken@8.5.9': + dependencies: + '@types/node': 25.1.0 + optional: true + + '@types/less@3.0.6': {} + + '@types/long@4.0.2': optional: true - /@types/less@3.0.3: - resolution: - { - integrity: sha512-1YXyYH83h6We1djyoUEqTlVyQtCfJAFXELSKW2ZRtjHD4hQ82CC4lvrv5D0l0FLcKBaiPbXyi3MpMsI9ZRgKsw==, - } - dev: true - - /@types/long@4.0.2: - resolution: - { - integrity: sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==, - } - dev: false - - /@types/mime@1.3.3: - resolution: - { - integrity: sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg==, - } - - /@types/mime@3.0.2: - resolution: - { - integrity: sha512-Wj+fqpTLtTbG7c0tH47dkahefpLKEbB+xAZuLq7b4/IDHPl/n6VoXcyUQ2bypFlbSwvCr0y+bD4euTTqTJsPxQ==, - } - - /@types/minimist@1.2.3: - resolution: - { - integrity: sha512-ZYFzrvyWUNhaPomn80dsMNgMeXxNWZBdkuG/hWlUvXvbdUH8ZERNBGXnU87McuGcWDsyzX2aChCv/SVN348k3A==, - } - dev: true - - /@types/node@12.20.55: - resolution: - { - integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==, - } - dev: false - - /@types/node@16.18.55: - resolution: - { - integrity: sha512-Y1zz/LIuJek01+hlPNzzXQhmq/Z2BCP96j18MSXC0S0jSu/IG4FFxmBs7W4/lI2vPJ7foVfEB0hUVtnOjnCiTg==, - } - - /@types/node@20.5.1: - resolution: - { - integrity: sha512-4tT2UrL5LBqDwoed9wZ6N3umC4Yhz3W3FloMmiiG4JwmUJWpie0c7lcnUNd4gtMKuDEO4wRVS8B6Xa0uMRsMKg==, - } - dev: true - - /@types/node@20.8.0: - resolution: - { - integrity: sha512-LzcWltT83s1bthcvjBmiBvGJiiUe84NWRHkw+ZV6Fr41z2FbIzvc815dk2nQ3RAKMuN2fkenM/z3Xv2QzEpYxQ==, - } - - /@types/normalize-package-data@2.4.2: - resolution: - { - integrity: sha512-lqa4UEhhv/2sjjIQgjX8B+RBjj47eo0mzGasklVJ78UKGQY1r0VpB9XHDaZZO9qzEFDdy4MrXLuEaSmPrPSe/A==, - } - dev: true - - /@types/optimize-css-assets-webpack-plugin@5.0.5: - resolution: - { - integrity: sha512-txpFQcyPPVaXFgTL7aTy43CqbQkvtzWXpzS/n663Lz8QB7qYDbQau/O+URRsvt96/u4QsjZLsZ/4KW7UnPfiYw==, - } - dependencies: - '@types/webpack': 4.41.33 - dev: true - - /@types/parse-json@4.0.0: - resolution: - { - integrity: sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==, - } - - /@types/prettier@2.7.3: - resolution: - { - integrity: sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==, - } - dev: true - - /@types/pug@2.0.6: - resolution: - { - integrity: sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==, - } - dev: true - - /@types/qs@6.9.8: - resolution: - { - integrity: sha512-u95svzDlTysU5xecFNTgfFG5RUWu1A9P0VzgpcIiGZA9iraHOdSzcxMxQ55DyeRaGCSxQi7LxXDI4rzq/MYfdg==, - } - - /@types/range-parser@1.2.5: - resolution: - { - integrity: sha512-xrO9OoVPqFuYyR/loIHjnbvvyRZREYKLjxV4+dY6v3FQR3stQ9ZxIGkaclF7YhI9hfjpuTbu14hZEy94qKLtOA==, - } - - /@types/relateurl@0.2.30: - resolution: - { - integrity: sha512-OzTBOmeBmS6FV7uFdbbUaE/rqWNcTrOTfRpDMWMczHa8nvNZi4exDD7d3ZKcxHH2GbgZ50dJkNhx9spRxSxmGg==, - } - dev: true - - /@types/sax@1.2.5: - resolution: - { - integrity: sha512-9jWta97bBVC027/MShr3gLab8gPhKy4l6qpb+UJLF5pDm3501NvA7uvqVCW+REFtx00oTi6Cq9JzLwgq6evVgw==, - } - dependencies: - '@types/node': 20.8.0 - dev: false - - /@types/semver@7.5.3: - resolution: - { - integrity: sha512-OxepLK9EuNEIPxWNME+C6WwbRAOOI2o2BaQEGzz5Lu2e4Z5eDnEo+/aVEDMIXywoJitJ7xWd641wrGLZdtwRyw==, - } - dev: true - - /@types/send@0.17.2: - resolution: - { - integrity: sha512-aAG6yRf6r0wQ29bkS+x97BIs64ZLxeE/ARwyS6wrldMm3C1MdKwCcnnEwMC1slI8wuxJOpiUH9MioC0A0i+GJw==, - } + '@types/mime@1.3.3': {} + + '@types/minimist@1.2.3': {} + + '@types/node@12.20.55': {} + + '@types/node@16.18.55': {} + + '@types/node@24.6.2': + dependencies: + undici-types: 7.13.0 + + '@types/node@25.1.0': + dependencies: + undici-types: 7.16.0 + + '@types/normalize-package-data@2.4.2': {} + + '@types/optimize-css-assets-webpack-plugin@5.0.8': + dependencies: + '@types/webpack': 4.41.38 + + '@types/parse-json@4.0.0': {} + + '@types/pug@2.0.10': {} + + '@types/qrcode@1.5.6': + dependencies: + '@types/node': 25.1.0 + + '@types/qs@6.9.8': {} + + '@types/range-parser@1.2.5': {} + + '@types/sax@1.2.7': + dependencies: + '@types/node': 25.1.0 + + '@types/semver@7.5.3': {} + + '@types/send@0.17.2': dependencies: '@types/mime': 1.3.3 - '@types/node': 20.8.0 + '@types/node': 25.1.0 - /@types/serve-static@1.15.1: - resolution: - { - integrity: sha512-NUo5XNiAdULrJENtJXZZ3fHtfMolzZwczzBbnAeBbqBwG+LaG6YaJtuwzwGSQZ2wsCrxjEhNNjAkKigy3n8teQ==, - } + '@types/serve-static@1.15.7': dependencies: - '@types/mime': 3.0.2 + '@types/http-errors': 2.0.4 '@types/node': 16.18.55 + '@types/send': 0.17.2 + + '@types/source-list-map@0.1.3': {} - /@types/source-list-map@0.1.3: - resolution: - { - integrity: sha512-I9R/7fUjzUOyDy6AFkehCK711wWoAXEaBi80AfjZt1lIkbe6AcXKd3ckQc3liMvQExWvfOeh/8CtKzrfUFN5gA==, - } - - /@types/stack-utils@2.0.1: - resolution: - { - integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==, - } - dev: true - - /@types/strip-bom@3.0.0: - resolution: - { - integrity: sha512-xevGOReSYGM7g/kUBZzPqCrR/KYAo+F0yiPc85WFTJa0MSLtyFTVTU6cJu/aV4mid7IffDIWqo69THF2o4JiEQ==, - } - dev: true - - /@types/strip-json-comments@0.0.30: - resolution: - { - integrity: sha512-7NQmHra/JILCd1QqpSzl8+mJRc8ZHz3uDm8YV1Ks9IhK0epEiTw8aIErbvH9PI+6XbqhyIQy3462nEsn7UVzjQ==, - } - dev: true - - /@types/tapable@1.0.9: - resolution: - { - integrity: sha512-fOHIwZua0sRltqWzODGUM6b4ffZrf/vzGUmNXdR+4DzuJP42PMbM5dLKcdzlYvv8bMJ3GALOzkk1q7cDm2zPyA==, - } - - /@types/terser-webpack-plugin@4.2.1: - resolution: - { - integrity: sha512-x688KsgQKJF8PPfv4qSvHQztdZNHLlWJdolN9/ptAGimHVy3rY+vHdfglQDFh1Z39h7eMWOd6fQ7ke3PKQcdyA==, - } - dependencies: - '@types/webpack': 4.41.33 + '@types/stack-utils@2.0.3': {} + + '@types/strip-bom@3.0.0': {} + + '@types/strip-json-comments@0.0.30': {} + + '@types/tapable@1.0.9': {} + + '@types/terser-webpack-plugin@4.2.1': + dependencies: + '@types/webpack': 4.41.38 terser: 4.8.1 - dev: true - /@types/uglify-js@3.17.2: - resolution: - { - integrity: sha512-9SjrHO54LINgC/6Ehr81NjAxAYvwEZqjUHLjJYvC4Nmr9jbLQCIZbWSvl4vXQkkmR1UAuaKDycau3O1kWGFyXQ==, - } + '@types/tough-cookie@4.0.5': {} + + '@types/uglify-js@3.17.2': dependencies: source-map: 0.6.1 - /@types/webpack-bundle-analyzer@3.9.5: - resolution: - { - integrity: sha512-QlyDyX7rsOIJHASzXWlih8DT9fR+XCG9cwIV/4pKrtScdHv4XFshdEf/7iiqLqG0lzWcoBdzG8ylMHQ5XLNixw==, - } + '@types/webpack-bundle-analyzer@3.9.5': dependencies: - '@types/webpack': 4.41.33 - dev: true + '@types/webpack': 4.41.38 - /@types/webpack-hot-middleware@2.25.5: - resolution: - { - integrity: sha512-/eRWWMgZteNzl17qLCRdRmtKPZuWy984b11Igz9+BAU5a99Hc2AJinnMohMPVahGRSHby4XwsnjlgIt9m0Ce3g==, - } + '@types/webpack-hot-middleware@2.25.5': dependencies: - '@types/connect': 3.4.35 - '@types/webpack': 4.41.33 - dev: true + '@types/connect': 3.4.38 + '@types/webpack': 4.41.38 - /@types/webpack-sources@3.2.1: - resolution: - { - integrity: sha512-iLC3Fsx62ejm3ST3PQ8vBMC54Rb3EoCprZjeJGI5q+9QjfDLGt9jeg/k245qz1G9AQnORGk0vqPicJFPT1QODQ==, - } + '@types/webpack-sources@3.2.1': dependencies: - '@types/node': 20.8.0 + '@types/node': 25.1.0 '@types/source-list-map': 0.1.3 - source-map: 0.7.4 + source-map: 0.7.6 - /@types/webpack@4.41.33: - resolution: - { - integrity: sha512-PPajH64Ft2vWevkerISMtnZ8rTs4YmRbs+23c402J0INmxDKCrhZNvwZYtzx96gY2wAtXdrK1BS2fiC8MlLr3g==, - } + '@types/webpack@4.41.38': dependencies: '@types/node': 16.18.55 '@types/tapable': 1.0.9 @@ -5850,358 +12528,248 @@ packages: '@types/webpack-sources': 3.2.1 anymatch: 3.1.3 source-map: 0.6.1 - dev: true - /@types/webpack@4.41.34: - resolution: - { - integrity: sha512-CN2aOGrR3zbMc2v+cKqzaClYP1ldkpPOgtdNvgX+RmlWCSWxHxpzz6WSCVQZRkF8D60ROlkRzAoEpgjWQ+bd2g==, - } + '@types/yargs-parser@21.0.3': {} + + '@types/yargs@17.0.33': dependencies: - '@types/node': 20.8.0 - '@types/tapable': 1.0.9 - '@types/uglify-js': 3.17.2 - '@types/webpack-sources': 3.2.1 - anymatch: 3.1.3 - source-map: 0.6.1 - dev: false - - /@types/yargs-parser@21.0.1: - resolution: - { - integrity: sha512-axdPBuLuEJt0c4yI5OZssC19K2Mq1uKdrfZBzuxLvaztgqUtFYZUNw7lETExPYJR9jdEoIg4mb7RQKRQzOkeGQ==, - } - dev: true - - /@types/yargs@16.0.6: - resolution: - { - integrity: sha512-oTP7/Q13GSPrgcwEwdlnkoZSQ1Hg9THe644qq8PG6hhJzjZ3qj1JjEFPIwWV/IXVs5XGIVqtkNOS9kh63WIJ+A==, - } - dependencies: - '@types/yargs-parser': 21.0.1 - dev: true - - /@types/yargs@17.0.26: - resolution: - { - integrity: sha512-Y3vDy2X6zw/ZCumcwLpdhM5L7jmyGpmBCTYMHDLqT2IKVMYRRLdv6ZakA+wxhra6Z/3bwhNbNl9bDGXaFU+6rw==, - } - dependencies: - '@types/yargs-parser': 21.0.1 - dev: true - - /@typescript-eslint/eslint-plugin@6.7.3(@typescript-eslint/parser@6.7.3)(eslint@8.53.0)(typescript@4.9.5): - resolution: - { - integrity: sha512-vntq452UHNltxsaaN+L9WyuMch8bMd9CqJ3zhzTPXXidwbf5mqqKCVXEuvRZUqLJSTLeWE65lQwyXsRGnXkCTA==, - } - engines: { node: ^16.0.0 || >=18.0.0 } - peerDependencies: - '@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha - eslint: ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@types/yargs-parser': 21.0.3 + + '@typescript-eslint/eslint-plugin@6.7.3(@typescript-eslint/parser@6.7.3(eslint@8.57.1)(typescript@4.9.5))(eslint@8.57.1)(typescript@4.9.5)': dependencies: '@eslint-community/regexpp': 4.9.0 - '@typescript-eslint/parser': 6.7.3(eslint@8.53.0)(typescript@4.9.5) + '@typescript-eslint/parser': 6.7.3(eslint@8.57.1)(typescript@4.9.5) '@typescript-eslint/scope-manager': 6.7.3 - '@typescript-eslint/type-utils': 6.7.3(eslint@8.53.0)(typescript@4.9.5) - '@typescript-eslint/utils': 6.7.3(eslint@8.53.0)(typescript@4.9.5) + '@typescript-eslint/type-utils': 6.7.3(eslint@8.57.1)(typescript@4.9.5) + '@typescript-eslint/utils': 6.7.3(eslint@8.57.1)(typescript@4.9.5) '@typescript-eslint/visitor-keys': 6.7.3 - debug: 4.3.4 - eslint: 8.53.0 + debug: 4.4.1 + eslint: 8.57.1 graphemer: 1.4.0 - ignore: 5.2.4 + ignore: 5.3.1 natural-compare: 1.4.0 - semver: 7.5.4 + semver: 7.7.3 ts-api-utils: 1.0.3(typescript@4.9.5) + optionalDependencies: typescript: 4.9.5 transitivePeerDependencies: - supports-color - dev: true - /@typescript-eslint/parser@6.7.3(eslint@8.53.0)(typescript@4.9.5): - resolution: - { - integrity: sha512-TlutE+iep2o7R8Lf+yoer3zU6/0EAUc8QIBB3GYBc1KGz4c4TRm83xwXUZVPlZ6YCLss4r77jbu6j3sendJoiQ==, - } - engines: { node: ^16.0.0 || >=18.0.0 } - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/parser@6.7.3(eslint@8.57.1)(typescript@4.9.5)': dependencies: '@typescript-eslint/scope-manager': 6.7.3 '@typescript-eslint/types': 6.7.3 '@typescript-eslint/typescript-estree': 6.7.3(typescript@4.9.5) '@typescript-eslint/visitor-keys': 6.7.3 - debug: 4.3.4 - eslint: 8.53.0 + debug: 4.4.1 + eslint: 8.57.1 + optionalDependencies: typescript: 4.9.5 transitivePeerDependencies: - supports-color - dev: true - /@typescript-eslint/scope-manager@6.7.3: - resolution: - { - integrity: sha512-wOlo0QnEou9cHO2TdkJmzF7DFGvAKEnB82PuPNHpT8ZKKaZu6Bm63ugOTn9fXNJtvuDPanBc78lGUGGytJoVzQ==, - } - engines: { node: ^16.0.0 || >=18.0.0 } + '@typescript-eslint/scope-manager@6.7.3': dependencies: '@typescript-eslint/types': 6.7.3 '@typescript-eslint/visitor-keys': 6.7.3 - dev: true - /@typescript-eslint/type-utils@6.7.3(eslint@8.53.0)(typescript@4.9.5): - resolution: - { - integrity: sha512-Fc68K0aTDrKIBvLnKTZ5Pf3MXK495YErrbHb1R6aTpfK5OdSFj0rVN7ib6Tx6ePrZ2gsjLqr0s98NG7l96KSQw==, - } - engines: { node: ^16.0.0 || >=18.0.0 } - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + '@typescript-eslint/type-utils@6.7.3(eslint@8.57.1)(typescript@4.9.5)': dependencies: '@typescript-eslint/typescript-estree': 6.7.3(typescript@4.9.5) - '@typescript-eslint/utils': 6.7.3(eslint@8.53.0)(typescript@4.9.5) - debug: 4.3.4 - eslint: 8.53.0 + '@typescript-eslint/utils': 6.7.3(eslint@8.57.1)(typescript@4.9.5) + debug: 4.4.1 + eslint: 8.57.1 ts-api-utils: 1.0.3(typescript@4.9.5) + optionalDependencies: typescript: 4.9.5 transitivePeerDependencies: - supports-color - dev: true - - /@typescript-eslint/types@6.7.3: - resolution: - { - integrity: sha512-4g+de6roB2NFcfkZb439tigpAMnvEIg3rIjWQ+EM7IBaYt/CdJt6em9BJ4h4UpdgaBWdmx2iWsafHTrqmgIPNw==, - } - engines: { node: ^16.0.0 || >=18.0.0 } - dev: true - - /@typescript-eslint/typescript-estree@6.7.3(typescript@4.9.5): - resolution: - { - integrity: sha512-YLQ3tJoS4VxLFYHTw21oe1/vIZPRqAO91z6Uv0Ss2BKm/Ag7/RVQBcXTGcXhgJMdA4U+HrKuY5gWlJlvoaKZ5g==, - } - engines: { node: ^16.0.0 || >=18.0.0 } - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true + + '@typescript-eslint/types@6.7.3': {} + + '@typescript-eslint/typescript-estree@6.7.3(typescript@4.9.5)': dependencies: '@typescript-eslint/types': 6.7.3 '@typescript-eslint/visitor-keys': 6.7.3 - debug: 4.3.4 + debug: 4.4.1 globby: 11.1.0 is-glob: 4.0.3 - semver: 7.5.4 + semver: 7.7.3 ts-api-utils: 1.0.3(typescript@4.9.5) + optionalDependencies: typescript: 4.9.5 transitivePeerDependencies: - supports-color - dev: true - /@typescript-eslint/utils@6.7.3(eslint@8.53.0)(typescript@4.9.5): - resolution: - { - integrity: sha512-vzLkVder21GpWRrmSR9JxGZ5+ibIUSudXlW52qeKpzUEQhRSmyZiVDDj3crAth7+5tmN1ulvgKaCU2f/bPRCzg==, - } - engines: { node: ^16.0.0 || >=18.0.0 } - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 + '@typescript-eslint/utils@6.7.3(eslint@8.57.1)(typescript@4.9.5)': dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) - '@types/json-schema': 7.0.13 + '@eslint-community/eslint-utils': 4.7.0(eslint@8.57.1) + '@types/json-schema': 7.0.15 '@types/semver': 7.5.3 '@typescript-eslint/scope-manager': 6.7.3 '@typescript-eslint/types': 6.7.3 '@typescript-eslint/typescript-estree': 6.7.3(typescript@4.9.5) - eslint: 8.53.0 - semver: 7.5.4 + eslint: 8.57.1 + semver: 7.7.3 transitivePeerDependencies: - supports-color - typescript - dev: true - /@typescript-eslint/visitor-keys@6.7.3: - resolution: - { - integrity: sha512-HEVXkU9IB+nk9o63CeICMHxFWbHWr3E1mpilIQBe9+7L/lH97rleFLVtYsfnWB+JVMaiFnEaxvknvmIzX+CqVg==, - } - engines: { node: ^16.0.0 || >=18.0.0 } + '@typescript-eslint/visitor-keys@6.7.3': + dependencies: + '@typescript-eslint/types': 6.7.3 + eslint-visitor-keys: 3.4.3 + + '@ungap/structured-clone@1.2.0': {} + + '@ungap/structured-clone@1.3.0': {} + + '@unrs/resolver-binding-android-arm-eabi@1.11.1': + optional: true + + '@unrs/resolver-binding-android-arm64@1.11.1': + optional: true + + '@unrs/resolver-binding-darwin-arm64@1.11.1': + optional: true + + '@unrs/resolver-binding-darwin-x64@1.11.1': + optional: true + + '@unrs/resolver-binding-freebsd-x64@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-x64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-wasm32-wasi@1.11.1': dependencies: - '@typescript-eslint/types': 6.7.3 - eslint-visitor-keys: 3.4.3 - dev: true - - /@ungap/structured-clone@1.2.0: - resolution: - { - integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==, - } - dev: true - - /@vue/babel-helper-vue-jsx-merge-props@1.4.0: - resolution: - { - integrity: sha512-JkqXfCkUDp4PIlFdDQ0TdXoIejMtTHP67/pvxlgeY+u5k3LEdKuWZ3LK6xkxo52uDoABIVyRwqVkfLQJhk7VBA==, - } - dev: false - - /@vue/babel-plugin-transform-vue-jsx@1.4.0(@babel/core@7.23.2): - resolution: - { - integrity: sha512-Fmastxw4MMx0vlgLS4XBX0XiBbUFzoMGeVXuMV08wyOfXdikAFqBTuYPR0tlk+XskL19EzHc39SgjrPGY23JnA==, - } - peerDependencies: - '@babel/core': ^7.0.0-0 + '@napi-rs/wasm-runtime': 0.2.12 + optional: true + + '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + optional: true + + '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + optional: true + + '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + optional: true + + '@vue/babel-helper-vue-jsx-merge-props@1.4.0': {} + + '@vue/babel-plugin-transform-vue-jsx@1.4.0(@babel/core@7.24.7)': dependencies: - '@babel/core': 7.23.2 - '@babel/helper-module-imports': 7.22.15 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.2) + '@babel/core': 7.24.7 + '@babel/helper-module-imports': 7.24.7 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.24.7) '@vue/babel-helper-vue-jsx-merge-props': 1.4.0 html-tags: 2.0.0 lodash.kebabcase: 4.1.1 svg-tags: 1.0.0 - dev: false + transitivePeerDependencies: + - supports-color - /@vue/babel-preset-jsx@1.4.0(@babel/core@7.23.2)(vue@2.7.15): - resolution: - { - integrity: sha512-QmfRpssBOPZWL5xw7fOuHNifCQcNQC1PrOo/4fu6xlhlKJJKSA3HqX92Nvgyx8fqHZTUGMPHmFA+IDqwXlqkSA==, - } - peerDependencies: - '@babel/core': ^7.0.0-0 - vue: '*' - peerDependenciesMeta: - vue: - optional: true + '@vue/babel-preset-jsx@1.4.0(@babel/core@7.24.7)(vue@2.7.16)': dependencies: - '@babel/core': 7.23.2 + '@babel/core': 7.24.7 '@vue/babel-helper-vue-jsx-merge-props': 1.4.0 - '@vue/babel-plugin-transform-vue-jsx': 1.4.0(@babel/core@7.23.2) - '@vue/babel-sugar-composition-api-inject-h': 1.4.0(@babel/core@7.23.2) - '@vue/babel-sugar-composition-api-render-instance': 1.4.0(@babel/core@7.23.2) - '@vue/babel-sugar-functional-vue': 1.4.0(@babel/core@7.23.2) - '@vue/babel-sugar-inject-h': 1.4.0(@babel/core@7.23.2) - '@vue/babel-sugar-v-model': 1.4.0(@babel/core@7.23.2) - '@vue/babel-sugar-v-on': 1.4.0(@babel/core@7.23.2) - vue: 2.7.15 - dev: false - - /@vue/babel-sugar-composition-api-inject-h@1.4.0(@babel/core@7.23.2): - resolution: - { - integrity: sha512-VQq6zEddJHctnG4w3TfmlVp5FzDavUSut/DwR0xVoe/mJKXyMcsIibL42wPntozITEoY90aBV0/1d2KjxHU52g==, - } - peerDependencies: - '@babel/core': ^7.0.0-0 + '@vue/babel-plugin-transform-vue-jsx': 1.4.0(@babel/core@7.24.7) + '@vue/babel-sugar-composition-api-inject-h': 1.4.0(@babel/core@7.24.7) + '@vue/babel-sugar-composition-api-render-instance': 1.4.0(@babel/core@7.24.7) + '@vue/babel-sugar-functional-vue': 1.4.0(@babel/core@7.24.7) + '@vue/babel-sugar-inject-h': 1.4.0(@babel/core@7.24.7) + '@vue/babel-sugar-v-model': 1.4.0(@babel/core@7.24.7) + '@vue/babel-sugar-v-on': 1.4.0(@babel/core@7.24.7) + optionalDependencies: + vue: 2.7.16 + transitivePeerDependencies: + - supports-color + + '@vue/babel-sugar-composition-api-inject-h@1.4.0(@babel/core@7.24.7)': dependencies: - '@babel/core': 7.23.2 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.2) - dev: false + '@babel/core': 7.24.7 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.24.7) - /@vue/babel-sugar-composition-api-render-instance@1.4.0(@babel/core@7.23.2): - resolution: - { - integrity: sha512-6ZDAzcxvy7VcnCjNdHJ59mwK02ZFuP5CnucloidqlZwVQv5CQLijc3lGpR7MD3TWFi78J7+a8J56YxbCtHgT9Q==, - } - peerDependencies: - '@babel/core': ^7.0.0-0 + '@vue/babel-sugar-composition-api-render-instance@1.4.0(@babel/core@7.24.7)': dependencies: - '@babel/core': 7.23.2 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.2) - dev: false + '@babel/core': 7.24.7 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.24.7) - /@vue/babel-sugar-functional-vue@1.4.0(@babel/core@7.23.2): - resolution: - { - integrity: sha512-lTEB4WUFNzYt2In6JsoF9sAYVTo84wC4e+PoZWSgM6FUtqRJz7wMylaEhSRgG71YF+wfLD6cc9nqVeXN2rwBvw==, - } - peerDependencies: - '@babel/core': ^7.0.0-0 + '@vue/babel-sugar-functional-vue@1.4.0(@babel/core@7.24.7)': dependencies: - '@babel/core': 7.23.2 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.2) - dev: false + '@babel/core': 7.24.7 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.24.7) - /@vue/babel-sugar-inject-h@1.4.0(@babel/core@7.23.2): - resolution: - { - integrity: sha512-muwWrPKli77uO2fFM7eA3G1lAGnERuSz2NgAxuOLzrsTlQl8W4G+wwbM4nB6iewlKbwKRae3nL03UaF5ffAPMA==, - } - peerDependencies: - '@babel/core': ^7.0.0-0 + '@vue/babel-sugar-inject-h@1.4.0(@babel/core@7.24.7)': dependencies: - '@babel/core': 7.23.2 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.2) - dev: false + '@babel/core': 7.24.7 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.24.7) - /@vue/babel-sugar-v-model@1.4.0(@babel/core@7.23.2): - resolution: - { - integrity: sha512-0t4HGgXb7WHYLBciZzN5s0Hzqan4Ue+p/3FdQdcaHAb7s5D9WZFGoSxEZHrR1TFVZlAPu1bejTKGeAzaaG3NCQ==, - } - peerDependencies: - '@babel/core': ^7.0.0-0 + '@vue/babel-sugar-v-model@1.4.0(@babel/core@7.24.7)': dependencies: - '@babel/core': 7.23.2 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.2) + '@babel/core': 7.24.7 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.24.7) '@vue/babel-helper-vue-jsx-merge-props': 1.4.0 - '@vue/babel-plugin-transform-vue-jsx': 1.4.0(@babel/core@7.23.2) + '@vue/babel-plugin-transform-vue-jsx': 1.4.0(@babel/core@7.24.7) camelcase: 5.3.1 html-tags: 2.0.0 svg-tags: 1.0.0 - dev: false + transitivePeerDependencies: + - supports-color - /@vue/babel-sugar-v-on@1.4.0(@babel/core@7.23.2): - resolution: - { - integrity: sha512-m+zud4wKLzSKgQrWwhqRObWzmTuyzl6vOP7024lrpeJM4x2UhQtRDLgYjXAw9xBXjCwS0pP9kXjg91F9ZNo9JA==, - } - peerDependencies: - '@babel/core': ^7.0.0-0 + '@vue/babel-sugar-v-on@1.4.0(@babel/core@7.24.7)': dependencies: - '@babel/core': 7.23.2 - '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.2) - '@vue/babel-plugin-transform-vue-jsx': 1.4.0(@babel/core@7.23.2) + '@babel/core': 7.24.7 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.24.7) + '@vue/babel-plugin-transform-vue-jsx': 1.4.0(@babel/core@7.24.7) camelcase: 5.3.1 - dev: false + transitivePeerDependencies: + - supports-color - /@vue/compiler-sfc@2.7.15: - resolution: - { - integrity: sha512-FCvIEevPmgCgqFBH7wD+3B97y7u7oj/Wr69zADBf403Tui377bThTjBvekaZvlRr4IwUAu3M6hYZeULZFJbdYg==, - } + '@vue/compiler-sfc@2.7.16': dependencies: - '@babel/parser': 7.23.0 - postcss: 8.4.31 + '@babel/parser': 7.24.0 + postcss: 8.5.6 source-map: 0.6.1 + optionalDependencies: + prettier: 2.8.8 - /@vue/component-compiler-utils@3.3.0(babel-core@7.0.0-bridge.0)(lodash@4.17.21): - resolution: - { - integrity: sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ==, - } + '@vue/component-compiler-utils@3.3.0(babel-core@7.0.0-bridge.0(@babel/core@7.28.4))(ejs@3.1.10)(handlebars@4.7.8)(lodash@4.17.21)': dependencies: - consolidate: 0.15.1(babel-core@7.0.0-bridge.0)(lodash@4.17.21) + consolidate: 0.15.1(babel-core@7.0.0-bridge.0(@babel/core@7.28.4))(ejs@3.1.10)(handlebars@4.7.8)(lodash@4.17.21) hash-sum: 1.0.2 lru-cache: 4.1.5 merge-source-map: 1.1.0 postcss: 7.0.39 - postcss-selector-parser: 6.0.13 + postcss-selector-parser: 6.1.2 source-map: 0.6.1 vue-template-es2015-compiler: 1.9.1 optionalDependencies: @@ -6260,221 +12828,104 @@ packages: - velocityjs - walrus - whiskers - dev: false - /@vue/test-utils@1.3.6(vue-template-compiler@2.7.15)(vue@2.7.15): - resolution: - { - integrity: sha512-udMmmF1ts3zwxUJEIAj5ziioR900reDrt6C9H3XpWPsLBx2lpHKoA4BTdd9HNIYbkGltWw+JjWJ+5O6QBwiyEw==, - } - peerDependencies: - vue: 2.x - vue-template-compiler: ^2.x + '@vue/test-utils@1.3.6(vue-template-compiler@2.7.16)(vue@2.7.16)': dependencies: dom-event-types: 1.1.0 lodash: 4.17.21 pretty: 2.0.0 - vue: 2.7.15 - vue-template-compiler: 2.7.15 - dev: true + vue: 2.7.16 + vue-template-compiler: 2.7.16 - /@webassemblyjs/ast@1.11.6: - resolution: - { - integrity: sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==, - } + '@webassemblyjs/ast@1.14.1': dependencies: - '@webassemblyjs/helper-numbers': 1.11.6 - '@webassemblyjs/helper-wasm-bytecode': 1.11.6 + '@webassemblyjs/helper-numbers': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 - /@webassemblyjs/ast@1.9.0: - resolution: - { - integrity: sha512-C6wW5L+b7ogSDVqymbkkvuW9kruN//YisMED04xzeBBqjHa2FYnmvOlS6Xj68xWQRgWvI9cIglsjFowH/RJyEA==, - } + '@webassemblyjs/ast@1.9.0': dependencies: '@webassemblyjs/helper-module-context': 1.9.0 '@webassemblyjs/helper-wasm-bytecode': 1.9.0 '@webassemblyjs/wast-parser': 1.9.0 - dev: false - - /@webassemblyjs/floating-point-hex-parser@1.11.6: - resolution: - { - integrity: sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==, - } - - /@webassemblyjs/floating-point-hex-parser@1.9.0: - resolution: - { - integrity: sha512-TG5qcFsS8QB4g4MhrxK5TqfdNe7Ey/7YL/xN+36rRjl/BlGE/NcBvJcqsRgCP6Z92mRE+7N50pRIi8SmKUbcQA==, - } - dev: false - - /@webassemblyjs/helper-api-error@1.11.6: - resolution: - { - integrity: sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==, - } - - /@webassemblyjs/helper-api-error@1.9.0: - resolution: - { - integrity: sha512-NcMLjoFMXpsASZFxJ5h2HZRcEhDkvnNFOAKneP5RbKRzaWJN36NC4jqQHKwStIhGXu5mUWlUUk7ygdtrO8lbmw==, - } - dev: false - - /@webassemblyjs/helper-buffer@1.11.6: - resolution: - { - integrity: sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==, - } - - /@webassemblyjs/helper-buffer@1.9.0: - resolution: - { - integrity: sha512-qZol43oqhq6yBPx7YM3m9Bv7WMV9Eevj6kMi6InKOuZxhw+q9hOkvq5e/PpKSiLfyetpaBnogSbNCfBwyB00CA==, - } - dev: false - - /@webassemblyjs/helper-code-frame@1.9.0: - resolution: - { - integrity: sha512-ERCYdJBkD9Vu4vtjUYe8LZruWuNIToYq/ME22igL+2vj2dQ2OOujIZr3MEFvfEaqKoVqpsFKAGsRdBSBjrIvZA==, - } + + '@webassemblyjs/floating-point-hex-parser@1.13.2': {} + + '@webassemblyjs/floating-point-hex-parser@1.9.0': {} + + '@webassemblyjs/helper-api-error@1.13.2': {} + + '@webassemblyjs/helper-api-error@1.9.0': {} + + '@webassemblyjs/helper-buffer@1.14.1': {} + + '@webassemblyjs/helper-buffer@1.9.0': {} + + '@webassemblyjs/helper-code-frame@1.9.0': dependencies: '@webassemblyjs/wast-printer': 1.9.0 - dev: false - /@webassemblyjs/helper-fsm@1.9.0: - resolution: - { - integrity: sha512-OPRowhGbshCb5PxJ8LocpdX9Kl0uB4XsAjl6jH/dWKlk/mzsANvhwbiULsaiqT5GZGT9qinTICdj6PLuM5gslw==, - } - dev: false + '@webassemblyjs/helper-fsm@1.9.0': {} - /@webassemblyjs/helper-module-context@1.9.0: - resolution: - { - integrity: sha512-MJCW8iGC08tMk2enck1aPW+BE5Cw8/7ph/VGZxwyvGbJwjktKkDK7vy7gAmMDx88D7mhDTCNKAW5tED+gZ0W8g==, - } + '@webassemblyjs/helper-module-context@1.9.0': dependencies: '@webassemblyjs/ast': 1.9.0 - dev: false - /@webassemblyjs/helper-numbers@1.11.6: - resolution: - { - integrity: sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==, - } + '@webassemblyjs/helper-numbers@1.13.2': dependencies: - '@webassemblyjs/floating-point-hex-parser': 1.11.6 - '@webassemblyjs/helper-api-error': 1.11.6 + '@webassemblyjs/floating-point-hex-parser': 1.13.2 + '@webassemblyjs/helper-api-error': 1.13.2 '@xtuc/long': 4.2.2 - /@webassemblyjs/helper-wasm-bytecode@1.11.6: - resolution: - { - integrity: sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==, - } - - /@webassemblyjs/helper-wasm-bytecode@1.9.0: - resolution: - { - integrity: sha512-R7FStIzyNcd7xKxCZH5lE0Bqy+hGTwS3LJjuv1ZVxd9O7eHCedSdrId/hMOd20I+v8wDXEn+bjfKDLzTepoaUw==, - } - dev: false - - /@webassemblyjs/helper-wasm-section@1.11.6: - resolution: - { - integrity: sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g==, - } - dependencies: - '@webassemblyjs/ast': 1.11.6 - '@webassemblyjs/helper-buffer': 1.11.6 - '@webassemblyjs/helper-wasm-bytecode': 1.11.6 - '@webassemblyjs/wasm-gen': 1.11.6 - - /@webassemblyjs/helper-wasm-section@1.9.0: - resolution: - { - integrity: sha512-XnMB8l3ek4tvrKUUku+IVaXNHz2YsJyOOmz+MMkZvh8h1uSJpSen6vYnw3IoQ7WwEuAhL8Efjms1ZWjqh2agvw==, - } + '@webassemblyjs/helper-wasm-bytecode@1.13.2': {} + + '@webassemblyjs/helper-wasm-bytecode@1.9.0': {} + + '@webassemblyjs/helper-wasm-section@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/wasm-gen': 1.14.1 + + '@webassemblyjs/helper-wasm-section@1.9.0': dependencies: '@webassemblyjs/ast': 1.9.0 '@webassemblyjs/helper-buffer': 1.9.0 '@webassemblyjs/helper-wasm-bytecode': 1.9.0 '@webassemblyjs/wasm-gen': 1.9.0 - dev: false - /@webassemblyjs/ieee754@1.11.6: - resolution: - { - integrity: sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==, - } + '@webassemblyjs/ieee754@1.13.2': dependencies: '@xtuc/ieee754': 1.2.0 - /@webassemblyjs/ieee754@1.9.0: - resolution: - { - integrity: sha512-dcX8JuYU/gvymzIHc9DgxTzUUTLexWwt8uCTWP3otys596io0L5aW02Gb1RjYpx2+0Jus1h4ZFqjla7umFniTg==, - } + '@webassemblyjs/ieee754@1.9.0': dependencies: '@xtuc/ieee754': 1.2.0 - dev: false - /@webassemblyjs/leb128@1.11.6: - resolution: - { - integrity: sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==, - } + '@webassemblyjs/leb128@1.13.2': dependencies: '@xtuc/long': 4.2.2 - /@webassemblyjs/leb128@1.9.0: - resolution: - { - integrity: sha512-ENVzM5VwV1ojs9jam6vPys97B/S65YQtv/aanqnU7D8aSoHFX8GyhGg0CMfyKNIHBuAVjy3tlzd5QMMINa7wpw==, - } + '@webassemblyjs/leb128@1.9.0': dependencies: '@xtuc/long': 4.2.2 - dev: false - - /@webassemblyjs/utf8@1.11.6: - resolution: - { - integrity: sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==, - } - - /@webassemblyjs/utf8@1.9.0: - resolution: - { - integrity: sha512-GZbQlWtopBTP0u7cHrEx+73yZKrQoBMpwkGEIqlacljhXCkVM1kMQge/Mf+csMJAjEdSwhOyLAS0AoR3AG5P8w==, - } - dev: false - - /@webassemblyjs/wasm-edit@1.11.6: - resolution: - { - integrity: sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw==, - } - dependencies: - '@webassemblyjs/ast': 1.11.6 - '@webassemblyjs/helper-buffer': 1.11.6 - '@webassemblyjs/helper-wasm-bytecode': 1.11.6 - '@webassemblyjs/helper-wasm-section': 1.11.6 - '@webassemblyjs/wasm-gen': 1.11.6 - '@webassemblyjs/wasm-opt': 1.11.6 - '@webassemblyjs/wasm-parser': 1.11.6 - '@webassemblyjs/wast-printer': 1.11.6 - - /@webassemblyjs/wasm-edit@1.9.0: - resolution: - { - integrity: sha512-FgHzBm80uwz5M8WKnMTn6j/sVbqilPdQXTWraSjBwFXSYGirpkSWE2R9Qvz9tNiTKQvoKILpCuTjBKzOIm0nxw==, - } + + '@webassemblyjs/utf8@1.13.2': {} + + '@webassemblyjs/utf8@1.9.0': {} + + '@webassemblyjs/wasm-edit@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/helper-wasm-section': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-opt': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + '@webassemblyjs/wast-printer': 1.14.1 + + '@webassemblyjs/wasm-edit@1.9.0': dependencies: '@webassemblyjs/ast': 1.9.0 '@webassemblyjs/helper-buffer': 1.9.0 @@ -6484,74 +12935,47 @@ packages: '@webassemblyjs/wasm-opt': 1.9.0 '@webassemblyjs/wasm-parser': 1.9.0 '@webassemblyjs/wast-printer': 1.9.0 - dev: false - - /@webassemblyjs/wasm-gen@1.11.6: - resolution: - { - integrity: sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA==, - } - dependencies: - '@webassemblyjs/ast': 1.11.6 - '@webassemblyjs/helper-wasm-bytecode': 1.11.6 - '@webassemblyjs/ieee754': 1.11.6 - '@webassemblyjs/leb128': 1.11.6 - '@webassemblyjs/utf8': 1.11.6 - - /@webassemblyjs/wasm-gen@1.9.0: - resolution: - { - integrity: sha512-cPE3o44YzOOHvlsb4+E9qSqjc9Qf9Na1OO/BHFy4OI91XDE14MjFN4lTMezzaIWdPqHnsTodGGNP+iRSYfGkjA==, - } + + '@webassemblyjs/wasm-gen@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wasm-gen@1.9.0': dependencies: '@webassemblyjs/ast': 1.9.0 '@webassemblyjs/helper-wasm-bytecode': 1.9.0 '@webassemblyjs/ieee754': 1.9.0 '@webassemblyjs/leb128': 1.9.0 '@webassemblyjs/utf8': 1.9.0 - dev: false - /@webassemblyjs/wasm-opt@1.11.6: - resolution: - { - integrity: sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==, - } + '@webassemblyjs/wasm-opt@1.14.1': dependencies: - '@webassemblyjs/ast': 1.11.6 - '@webassemblyjs/helper-buffer': 1.11.6 - '@webassemblyjs/wasm-gen': 1.11.6 - '@webassemblyjs/wasm-parser': 1.11.6 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-buffer': 1.14.1 + '@webassemblyjs/wasm-gen': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 - /@webassemblyjs/wasm-opt@1.9.0: - resolution: - { - integrity: sha512-Qkjgm6Anhm+OMbIL0iokO7meajkzQD71ioelnfPEj6r4eOFuqm4YC3VBPqXjFyyNwowzbMD+hizmprP/Fwkl2A==, - } + '@webassemblyjs/wasm-opt@1.9.0': dependencies: '@webassemblyjs/ast': 1.9.0 '@webassemblyjs/helper-buffer': 1.9.0 '@webassemblyjs/wasm-gen': 1.9.0 '@webassemblyjs/wasm-parser': 1.9.0 - dev: false - - /@webassemblyjs/wasm-parser@1.11.6: - resolution: - { - integrity: sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ==, - } - dependencies: - '@webassemblyjs/ast': 1.11.6 - '@webassemblyjs/helper-api-error': 1.11.6 - '@webassemblyjs/helper-wasm-bytecode': 1.11.6 - '@webassemblyjs/ieee754': 1.11.6 - '@webassemblyjs/leb128': 1.11.6 - '@webassemblyjs/utf8': 1.11.6 - - /@webassemblyjs/wasm-parser@1.9.0: - resolution: - { - integrity: sha512-9+wkMowR2AmdSWQzsPEjFU7njh8HTO5MqO8vjwEHuM+AMHioNqSBONRdr0NQQ3dVQrzp0s8lTcYqzUdb7YgELA==, - } + + '@webassemblyjs/wasm-parser@1.14.1': + dependencies: + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/helper-api-error': 1.13.2 + '@webassemblyjs/helper-wasm-bytecode': 1.13.2 + '@webassemblyjs/ieee754': 1.13.2 + '@webassemblyjs/leb128': 1.13.2 + '@webassemblyjs/utf8': 1.13.2 + + '@webassemblyjs/wasm-parser@1.9.0': dependencies: '@webassemblyjs/ast': 1.9.0 '@webassemblyjs/helper-api-error': 1.9.0 @@ -6559,13 +12983,8 @@ packages: '@webassemblyjs/ieee754': 1.9.0 '@webassemblyjs/leb128': 1.9.0 '@webassemblyjs/utf8': 1.9.0 - dev: false - /@webassemblyjs/wast-parser@1.9.0: - resolution: - { - integrity: sha512-qsqSAP3QQ3LyZjNC/0jBJ/ToSxfYJ8kYyuiGvtn/8MK89VrNEfwj7BPQzJVHi0jGTRK2dGdJ5PRqhtjzoww+bw==, - } + '@webassemblyjs/wast-parser@1.9.0': dependencies: '@webassemblyjs/ast': 1.9.0 '@webassemblyjs/floating-point-hex-parser': 1.9.0 @@ -6573,877 +12992,360 @@ packages: '@webassemblyjs/helper-code-frame': 1.9.0 '@webassemblyjs/helper-fsm': 1.9.0 '@xtuc/long': 4.2.2 - dev: false - /@webassemblyjs/wast-printer@1.11.6: - resolution: - { - integrity: sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==, - } + '@webassemblyjs/wast-printer@1.14.1': dependencies: - '@webassemblyjs/ast': 1.11.6 + '@webassemblyjs/ast': 1.14.1 '@xtuc/long': 4.2.2 - /@webassemblyjs/wast-printer@1.9.0: - resolution: - { - integrity: sha512-2J0nE95rHXHyQ24cWjMKJ1tqB/ds8z/cyeOZxJhcb+rW+SQASVjuznUSmdz5GpVJTzU8JkhYut0D3siFDD6wsA==, - } + '@webassemblyjs/wast-printer@1.9.0': dependencies: '@webassemblyjs/ast': 1.9.0 '@webassemblyjs/wast-parser': 1.9.0 '@xtuc/long': 4.2.2 - dev: false - - /@xtuc/ieee754@1.2.0: - resolution: - { - integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==, - } - - /@xtuc/long@4.2.2: - resolution: - { - integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==, - } - - /JSONStream@1.3.5: - resolution: - { - integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==, - } - hasBin: true - dependencies: - jsonparse: 1.3.1 - through: 2.3.8 - dev: true - - /abab@2.0.6: - resolution: - { - integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==, - } - dev: true - - /abbrev@1.1.1: - resolution: - { - integrity: sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==, - } - dev: true - - /abort-controller@3.0.0: - resolution: - { - integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==, - } - engines: { node: '>=6.5' } - requiresBuild: true + + '@xtuc/ieee754@1.2.0': {} + + '@xtuc/long@4.2.2': {} + + abbrev@1.1.1: {} + + abort-controller@3.0.0: dependencies: event-target-shim: 5.0.1 - dev: false optional: true - /accepts@1.3.8: - resolution: - { - integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==, - } - engines: { node: '>= 0.6' } + accepts@1.3.8: dependencies: mime-types: 2.1.35 negotiator: 0.6.3 - dev: false - /acorn-globals@6.0.0: - resolution: - { - integrity: sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==, - } + acorn-import-phases@1.0.4(acorn@8.15.0): dependencies: - acorn: 7.4.1 - acorn-walk: 7.2.0 - dev: true + acorn: 8.15.0 - /acorn-import-assertions@1.9.0(acorn@8.10.0): - resolution: - { - integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==, - } - peerDependencies: - acorn: ^8 + acorn-jsx@5.3.2(acorn@8.15.0): dependencies: - acorn: 8.10.0 + acorn: 8.15.0 - /acorn-jsx@5.3.2(acorn@8.10.0): - resolution: - { - integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==, - } - peerDependencies: - acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - dependencies: - acorn: 8.10.0 - dev: true - - /acorn-walk@7.2.0: - resolution: - { - integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==, - } - engines: { node: '>=0.4.0' } - dev: true - - /acorn-walk@8.2.0: - resolution: - { - integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==, - } - engines: { node: '>=0.4.0' } - - /acorn@6.4.2: - resolution: - { - integrity: sha512-XtGIhXwF8YM8bJhGxG5kXgjkEuNGLTkoYqVE+KMR+aspr4KGYmKYg7yUe3KghyQ9yheNwLnjmzh/7+gfDBmHCQ==, - } - engines: { node: '>=0.4.0' } - hasBin: true - dev: false - - /acorn@7.4.1: - resolution: - { - integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==, - } - engines: { node: '>=0.4.0' } - hasBin: true - dev: true - - /acorn@8.10.0: - resolution: - { - integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==, - } - engines: { node: '>=0.4.0' } - hasBin: true + acorn-walk@8.2.0: {} + + acorn@6.4.2: {} - /agent-base@6.0.2: - resolution: - { - integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==, - } - engines: { node: '>= 6.0.0' } + acorn@8.15.0: {} + + agent-base@6.0.2: dependencies: - debug: 4.3.4 + debug: 4.4.3 transitivePeerDependencies: - supports-color - /aggregate-error@3.1.0: - resolution: - { - integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==, - } - engines: { node: '>=8' } + agent-base@7.1.4: {} + + aggregate-error@3.1.0: dependencies: clean-stack: 2.2.0 indent-string: 4.0.0 - dev: false - /ajv-errors@1.0.1(ajv@6.12.6): - resolution: - { - integrity: sha512-DCRfO/4nQ+89p/RK43i8Ezd41EqdGIU4ld7nGF8OQ14oc/we5rEntLCUa7+jrn3nn83BosfwZA0wb4pon2o8iQ==, - } - peerDependencies: - ajv: '>=5.0.0' + ajv-errors@1.0.1(ajv@6.12.6): dependencies: ajv: 6.12.6 - dev: false - /ajv-formats@2.1.1(ajv@8.12.0): - resolution: - { - integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==, - } - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - dependencies: - ajv: 8.12.0 + ajv-formats@2.1.1(ajv@8.17.1): + optionalDependencies: + ajv: 8.17.1 - /ajv-keywords@3.5.2(ajv@6.12.6): - resolution: - { - integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==, - } - peerDependencies: - ajv: ^6.9.1 + ajv-keywords@3.5.2(ajv@6.12.6): dependencies: ajv: 6.12.6 - /ajv-keywords@5.1.0(ajv@8.12.0): - resolution: - { - integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==, - } - peerDependencies: - ajv: ^8.8.2 + ajv-keywords@5.1.0(ajv@8.17.1): dependencies: - ajv: 8.12.0 + ajv: 8.17.1 fast-deep-equal: 3.1.3 - /ajv@6.12.6: - resolution: - { - integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==, - } + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 fast-json-stable-stringify: 2.1.0 json-schema-traverse: 0.4.1 uri-js: 4.4.1 - /ajv@8.12.0: - resolution: - { - integrity: sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==, - } + ajv@8.12.0: 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-align@3.0.1: - resolution: - { - integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==, - } + ajv@8.17.1: + dependencies: + fast-deep-equal: 3.1.3 + fast-uri: 3.1.0 + json-schema-traverse: 1.0.0 + require-from-string: 2.0.2 + + ansi-align@3.0.1: dependencies: string-width: 4.2.3 - dev: false - /ansi-escapes@4.3.2: - resolution: - { - integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==, - } - engines: { node: '>=8' } + ansi-escapes@4.3.2: dependencies: type-fest: 0.21.3 - /ansi-escapes@5.0.0: - resolution: - { - integrity: sha512-5GFMVX8HqE/TB+FuBJGuO5XG0WrsA6ptUqoODaT/n9mmUaZFkqnBueB4leqGBCmrUHnCnC4PCZTCd0E7QQ83bA==, - } - engines: { node: '>=12' } + ansi-escapes@7.1.1: dependencies: - type-fest: 1.4.0 - dev: true - - /ansi-html-community@0.0.8: - resolution: - { - integrity: sha512-1APHAyr3+PCamwNw3bXCPp4HFLONZt/yIH0sZp0/469KWNTEy+qN5jQ3GVX6DMZ1UXAi34yVwtTeaG/HpBuuzw==, - } - engines: { '0': node >= 0.8.0 } - hasBin: true - dev: false - - /ansi-regex@2.1.1: - resolution: - { - integrity: sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==, - } - engines: { node: '>=0.10.0' } - - /ansi-regex@5.0.1: - resolution: - { - integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==, - } - engines: { node: '>=8' } - - /ansi-regex@6.0.1: - resolution: - { - integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==, - } - engines: { node: '>=12' } - dev: true - - /ansi-styles@2.2.1: - resolution: - { - integrity: sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==, - } - engines: { node: '>=0.10.0' } - dev: true - - /ansi-styles@3.2.1: - resolution: - { - integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==, - } - engines: { node: '>=4' } + environment: 1.1.0 + + ansi-html-community@0.0.8: {} + + ansi-regex@2.1.1: {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.1.0: {} + + ansi-styles@2.2.1: {} + + ansi-styles@3.2.1: dependencies: color-convert: 1.9.3 - /ansi-styles@4.3.0: - resolution: - { - integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==, - } - engines: { node: '>=8' } + ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 - /ansi-styles@5.2.0: - resolution: - { - integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==, - } - engines: { node: '>=10' } - dev: true - - /ansi-styles@6.2.1: - resolution: - { - integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==, - } - engines: { node: '>=12' } - dev: true - - /anymatch@2.0.0: - resolution: - { - integrity: sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==, - } - requiresBuild: true + ansi-styles@5.2.0: {} + + ansi-styles@6.2.3: {} + + anymatch@2.0.0: dependencies: micromatch: 3.1.10 normalize-path: 2.1.1 transitivePeerDependencies: - supports-color - dev: false optional: true - /anymatch@3.1.3: - resolution: - { - integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==, - } - engines: { node: '>= 8' } + anymatch@3.1.3: dependencies: normalize-path: 3.0.0 picomatch: 2.3.1 - /aproba@1.2.0: - resolution: - { - integrity: sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==, - } - dev: false - - /arg@4.1.3: - resolution: - { - integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==, - } - - /arg@5.0.2: - resolution: - { - integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==, - } - dev: false - - /argparse@1.0.10: - resolution: - { - integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==, - } + aproba@1.2.0: {} + + arg@4.1.3: {} + + arg@5.0.2: {} + + argparse@1.0.10: dependencies: sprintf-js: 1.0.3 - dev: true - - /argparse@2.0.1: - resolution: - { - integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==, - } - dev: true - - /arr-diff@4.0.0: - resolution: - { - integrity: sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==, - } - engines: { node: '>=0.10.0' } - dev: false - - /arr-flatten@1.1.0: - resolution: - { - integrity: sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==, - } - engines: { node: '>=0.10.0' } - dev: false - - /arr-union@3.1.0: - resolution: - { - integrity: sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==, - } - engines: { node: '>=0.10.0' } - dev: false - - /array-buffer-byte-length@1.0.0: - resolution: - { - integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==, - } + + argparse@2.0.1: {} + + arr-diff@4.0.0: {} + + arr-flatten@1.1.0: {} + + arr-union@3.1.0: {} + + array-buffer-byte-length@1.0.0: dependencies: call-bind: 1.0.2 is-array-buffer: 3.0.2 - /array-ify@1.0.0: - resolution: - { - integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==, - } - dev: true + array-ify@1.0.0: {} - /array-includes@3.1.7: - resolution: - { - integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==, - } - engines: { node: '>= 0.4' } + array-includes@3.1.7: dependencies: call-bind: 1.0.2 define-properties: 1.2.1 es-abstract: 1.22.2 get-intrinsic: 1.2.1 is-string: 1.0.7 - dev: true - - /array-union@2.1.0: - resolution: - { - integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==, - } - engines: { node: '>=8' } - - /array-unique@0.3.2: - resolution: - { - integrity: sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==, - } - engines: { node: '>=0.10.0' } - dev: false - - /array.prototype.findlastindex@1.2.3: - resolution: - { - integrity: sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==, - } - engines: { node: '>= 0.4' } + + array-union@2.1.0: {} + + array-unique@0.3.2: {} + + array.prototype.findlastindex@1.2.3: dependencies: call-bind: 1.0.2 define-properties: 1.2.1 es-abstract: 1.22.2 es-shim-unscopables: 1.0.0 get-intrinsic: 1.2.1 - dev: true - /array.prototype.flat@1.3.2: - resolution: - { - integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==, - } - engines: { node: '>= 0.4' } + array.prototype.flat@1.3.2: dependencies: call-bind: 1.0.2 define-properties: 1.2.1 es-abstract: 1.22.2 es-shim-unscopables: 1.0.0 - dev: true - /array.prototype.flatmap@1.3.2: - resolution: - { - integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==, - } - engines: { node: '>= 0.4' } + array.prototype.flatmap@1.3.2: dependencies: call-bind: 1.0.2 define-properties: 1.2.1 es-abstract: 1.22.2 es-shim-unscopables: 1.0.0 - dev: true - /array.prototype.reduce@1.0.6: - resolution: - { - integrity: sha512-UW+Mz8LG/sPSU8jRDCjVr6J/ZKAGpHfwrZ6kWTG5qCxIEiXdVshqGnu5vEZA8S1y6X4aCSbQZ0/EEsfvEvBiSg==, - } - engines: { node: '>= 0.4' } + array.prototype.reduce@1.0.6: dependencies: call-bind: 1.0.2 define-properties: 1.2.1 es-abstract: 1.22.2 es-array-method-boxes-properly: 1.0.0 is-string: 1.0.7 - dev: false - /arraybuffer.prototype.slice@1.0.2: - resolution: - { - integrity: sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==, - } - engines: { node: '>= 0.4' } + arraybuffer.prototype.slice@1.0.2: dependencies: array-buffer-byte-length: 1.0.0 call-bind: 1.0.2 define-properties: 1.2.1 es-abstract: 1.22.2 - get-intrinsic: 1.2.1 + get-intrinsic: 1.3.0 is-array-buffer: 3.0.2 is-shared-array-buffer: 1.0.2 - /arrify@1.0.1: - resolution: - { - integrity: sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==, - } - engines: { node: '>=0.10.0' } - dev: true - - /arrify@2.0.1: - resolution: - { - integrity: sha512-3duEwti880xqi4eAMN8AyR4a0ByT90zoYdLlevfrvU43vb0YZwZVfxOgxWrLXXXpyugL0hNZc9G6BiB5B3nUug==, - } - engines: { node: '>=8' } - requiresBuild: true - dev: false + arrify@1.0.1: {} + + arrify@2.0.1: optional: true - /asn1.js@5.4.1: - resolution: - { - integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==, - } + asn1.js@5.4.1: dependencies: bn.js: 4.12.0 inherits: 2.0.4 minimalistic-assert: 1.0.1 safer-buffer: 2.1.2 - dev: false - /assert@1.5.1: - resolution: - { - integrity: sha512-zzw1uCAgLbsKwBfFc8CX78DDg+xZeBksSO3vwVIDDN5i94eOrPsSSyiVhmsSABFDM/OcpE2aagCat9dnWQLG1A==, - } + assert@1.5.1: dependencies: object.assign: 4.1.4 util: 0.10.4 - dev: false - - /assign-symbols@1.0.0: - resolution: - { - integrity: sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==, - } - engines: { node: '>=0.10.0' } - dev: false - - /astral-regex@2.0.0: - resolution: - { - integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==, - } - engines: { node: '>=8' } - dev: true - - /async-cache@1.1.0: - resolution: - { - integrity: sha512-YDQc4vBn5NFhY6g6HhVshyi3Fy9+SQ5ePnE7JLDJn1DoL+i7ER+vMwtTNOYk9leZkYMnOwpBCWqyLDPw8Aig8g==, - } - deprecated: No longer maintained. Use [lru-cache](http://npm.im/lru-cache) version 7.6 or higher, and provide an asynchronous `fetchMethod` option. + + assign-symbols@1.0.0: {} + + astral-regex@2.0.0: {} + + async-cache@1.1.0: dependencies: lru-cache: 4.1.5 - dev: false - - /async-each@1.0.6: - resolution: - { - integrity: sha512-c646jH1avxr+aVpndVMeAfYw7wAa6idufrlN3LPA4PmKS0QEGp6PIC9nwz0WQkkvBGAMEki3pFdtxaF39J9vvg==, - } - requiresBuild: true - dev: false + + async-each@1.0.6: optional: true - /async-retry@1.3.3: - resolution: - { - integrity: sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==, - } - requiresBuild: true + async-retry@1.3.3: dependencies: retry: 0.13.1 - dev: false optional: true - /asynckit@0.4.0: - resolution: - { - integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==, - } - dev: true - - /at-least-node@1.0.0: - resolution: - { - integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==, - } - engines: { node: '>= 4.0.0' } - dev: true - - /atob@2.1.2: - resolution: - { - integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==, - } - engines: { node: '>= 4.5.0' } - hasBin: true + async@3.2.6: + optional: true - /autoprefixer@10.4.16(postcss@8.4.31): - resolution: - { - integrity: sha512-7vd3UC6xKp0HLfua5IjZlcXvGAGy7cBAXTg2lyQ/8WpNhd6SiZ8Be+xm3FyBSYJx5GKcpRCzBh7RH4/0dnY+uQ==, - } - engines: { node: ^10 || ^12 || >=14 } - hasBin: true - peerDependencies: - postcss: ^8.1.0 + asynckit@0.4.0: {} + + at-least-node@1.0.0: {} + + atob@2.1.2: {} + + autoprefixer@10.4.19(postcss@8.5.6): dependencies: - browserslist: 4.22.1 - caniuse-lite: 1.0.30001561 - fraction.js: 4.3.6 + browserslist: 4.28.1 + caniuse-lite: 1.0.30001762 + fraction.js: 4.3.7 normalize-range: 0.1.2 - picocolors: 1.0.0 - postcss: 8.4.31 + picocolors: 1.1.1 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - - /available-typed-arrays@1.0.5: - resolution: - { - integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==, - } - engines: { node: '>= 0.4' } - - /axios@0.25.0: - resolution: - { - integrity: sha512-cD8FOb0tRH3uuEe6+evtAbgJtfxr7ly3fQjYcMcuPlgkwVS9xboaVIpcDV+cYQe+yGykgwZCs1pzjntcGa6l5g==, - } - dependencies: - follow-redirects: 1.15.3 + + available-typed-arrays@1.0.5: {} + + axios@0.31.0: + dependencies: + follow-redirects: 1.16.0 + form-data: 4.0.5 + proxy-from-env: 1.1.0 transitivePeerDependencies: - debug - dev: true - /babel-code-frame@6.26.0: - resolution: - { - integrity: sha512-XqYMR2dfdGMW+hd0IUZ2PwK+fGeFkOxZJ0wY+JaQAHzt1Zx8LcvpiZD2NiGkEG8qx0CfkAOr5xt76d1e8vG90g==, - } + babel-code-frame@6.26.0: dependencies: chalk: 1.1.3 esutils: 2.0.3 js-tokens: 3.0.2 - dev: true - /babel-core@7.0.0-bridge.0(@babel/core@7.23.2): - resolution: - { - integrity: sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==, - } - peerDependencies: - '@babel/core': ^7.0.0-0 + babel-core@7.0.0-bridge.0(@babel/core@7.28.4): dependencies: - '@babel/core': 7.23.2 + '@babel/core': 7.28.4 - /babel-jest@27.5.1(@babel/core@7.23.0): - resolution: - { - integrity: sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - peerDependencies: - '@babel/core': ^7.8.0 + babel-jest@30.2.0(@babel/core@7.28.4): dependencies: - '@babel/core': 7.23.0 - '@jest/transform': 27.5.1 - '@jest/types': 27.5.1 - '@types/babel__core': 7.20.2 - babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 27.5.1(@babel/core@7.23.0) - chalk: 4.1.2 - graceful-fs: 4.2.11 - slash: 3.0.0 - transitivePeerDependencies: - - supports-color - dev: true - - /babel-jest@29.7.0(@babel/core@7.23.2): - resolution: - { - integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==, - } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } - peerDependencies: - '@babel/core': ^7.8.0 - dependencies: - '@babel/core': 7.23.2 - '@jest/transform': 29.7.0 - '@types/babel__core': 7.20.2 - babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.23.2) + '@babel/core': 7.28.4 + '@jest/transform': 30.2.0 + '@types/babel__core': 7.20.5 + babel-plugin-istanbul: 7.0.1 + babel-preset-jest: 30.2.0(@babel/core@7.28.4) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 transitivePeerDependencies: - supports-color - dev: true - /babel-loader@8.3.0(@babel/core@7.23.2)(webpack@4.47.0): - resolution: - { - integrity: sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==, - } - engines: { node: '>= 8.9' } - peerDependencies: - '@babel/core': ^7.0.0 - webpack: '>=2' + babel-loader@8.3.0(@babel/core@7.24.7)(webpack@4.47.0): dependencies: - '@babel/core': 7.23.2 + '@babel/core': 7.24.7 find-cache-dir: 3.3.2 loader-utils: 2.0.4 make-dir: 3.1.0 schema-utils: 2.7.1 webpack: 4.47.0 - dev: false - /babel-messages@6.23.0: - resolution: - { - integrity: sha512-Bl3ZiA+LjqaMtNYopA9TYE9HP1tQ+E5dLxE0XrAzcIJeK2UqF0/EaqXwBn9esd4UmTfEab+P+UYQ1GnioFIb/w==, - } + babel-messages@6.23.0: dependencies: babel-runtime: 6.26.0 - dev: true - /babel-plugin-istanbul@6.1.1: - resolution: - { - integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==, - } - engines: { node: '>=8' } + babel-plugin-istanbul@7.0.1: dependencies: - '@babel/helper-plugin-utils': 7.22.5 + '@babel/helper-plugin-utils': 7.27.1 '@istanbuljs/load-nyc-config': 1.1.0 '@istanbuljs/schema': 0.1.3 - istanbul-lib-instrument: 5.2.1 + istanbul-lib-instrument: 6.0.3 test-exclude: 6.0.0 transitivePeerDependencies: - supports-color - dev: true - - /babel-plugin-jest-hoist@27.5.1: - resolution: - { - integrity: sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - dependencies: - '@babel/template': 7.22.15 - '@babel/types': 7.23.0 - '@types/babel__core': 7.20.2 - '@types/babel__traverse': 7.20.2 - dev: true - - /babel-plugin-jest-hoist@29.6.3: - resolution: - { - integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==, - } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } - dependencies: - '@babel/template': 7.22.15 - '@babel/types': 7.23.0 - '@types/babel__core': 7.20.2 - '@types/babel__traverse': 7.20.2 - dev: true - - /babel-plugin-polyfill-corejs2@0.4.6(@babel/core@7.23.2): - resolution: - { - integrity: sha512-jhHiWVZIlnPbEUKSSNb9YoWcQGdlTLq7z1GHL4AjFxaoOUMuuEVJ+Y4pAaQUGOGk93YsVCKPbqbfw3m0SM6H8Q==, - } - peerDependencies: - '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + + babel-plugin-jest-hoist@30.2.0: dependencies: - '@babel/compat-data': 7.23.2 - '@babel/core': 7.23.2 - '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.2) + '@types/babel__core': 7.20.5 + + babel-plugin-polyfill-corejs2@0.4.11(@babel/core@7.24.7): + dependencies: + '@babel/compat-data': 7.24.7 + '@babel/core': 7.24.7 + '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.7) semver: 6.3.1 transitivePeerDependencies: - supports-color - dev: false - /babel-plugin-polyfill-corejs3@0.8.6(@babel/core@7.23.2): - resolution: - { - integrity: sha512-leDIc4l4tUgU7str5BWLS2h8q2N4Nf6lGZP6UrNDxdtfF2g69eJ5L0H7S8A5Ln/arfFAfHor5InAdZuIOwZdgQ==, - } - peerDependencies: - '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + babel-plugin-polyfill-corejs3@0.10.4(@babel/core@7.24.7): dependencies: - '@babel/core': 7.23.2 - '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.2) - core-js-compat: 3.33.2 + '@babel/core': 7.24.7 + '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.7) + core-js-compat: 3.37.1 transitivePeerDependencies: - supports-color - dev: false - /babel-plugin-polyfill-regenerator@0.5.3(@babel/core@7.23.2): - resolution: - { - integrity: sha512-8sHeDOmXC8csczMrYEOf0UTNa4yE2SxV5JGeT/LP1n0OYVDUUFPxG9vdk2AlDlIit4t+Kf0xCtpgXPBwnn/9pw==, - } - peerDependencies: - '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 + babel-plugin-polyfill-regenerator@0.6.2(@babel/core@7.24.7): dependencies: - '@babel/core': 7.23.2 - '@babel/helper-define-polyfill-provider': 0.4.3(@babel/core@7.23.2) + '@babel/core': 7.24.7 + '@babel/helper-define-polyfill-provider': 0.6.2(@babel/core@7.24.7) transitivePeerDependencies: - supports-color - dev: false - /babel-plugin-transform-es2015-modules-commonjs@6.26.2: - resolution: - { - integrity: sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==, - } + babel-plugin-transform-es2015-modules-commonjs@6.26.2: dependencies: babel-plugin-transform-strict-mode: 6.24.1 babel-runtime: 6.26.0 @@ -7451,107 +13353,43 @@ packages: babel-types: 6.26.0 transitivePeerDependencies: - supports-color - dev: true - /babel-plugin-transform-strict-mode@6.24.1: - resolution: - { - integrity: sha512-j3KtSpjyLSJxNoCDrhwiJad8kw0gJ9REGj8/CqL0HeRyLnvUNYV9zcqluL6QJSXh3nfsLEmSLvwRfGzrgR96Pw==, - } + babel-plugin-transform-strict-mode@6.24.1: dependencies: babel-runtime: 6.26.0 babel-types: 6.26.0 - dev: true - - /babel-preset-current-node-syntax@1.0.1(@babel/core@7.23.0): - resolution: - { - integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==, - } - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.23.0 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.0) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.23.0) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.0) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.0) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.0) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.0) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.0) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.0) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.0) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.0) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.0) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.0) - dev: true - - /babel-preset-current-node-syntax@1.0.1(@babel/core@7.23.2): - resolution: - { - integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==, - } - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.23.2 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.2) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.23.2) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.2) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.2) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.2) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.2) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.2) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.2) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.2) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.2) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.2) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.2) - dev: true - - /babel-preset-jest@27.5.1(@babel/core@7.23.0): - resolution: - { - integrity: sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.23.0 - babel-plugin-jest-hoist: 27.5.1 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.0) - dev: true - - /babel-preset-jest@29.6.3(@babel/core@7.23.2): - resolution: - { - integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==, - } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } - peerDependencies: - '@babel/core': ^7.0.0 - dependencies: - '@babel/core': 7.23.2 - babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.2) - dev: true - /babel-runtime@6.26.0: - resolution: - { - integrity: sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==, - } + babel-preset-current-node-syntax@1.2.0(@babel/core@7.28.4): + dependencies: + '@babel/core': 7.28.4 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.28.4) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.28.4) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.28.4) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.28.4) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.28.4) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.28.4) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.28.4) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.28.4) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.28.4) + + babel-preset-jest@30.2.0(@babel/core@7.28.4): + dependencies: + '@babel/core': 7.28.4 + babel-plugin-jest-hoist: 30.2.0 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.4) + + babel-runtime@6.26.0: dependencies: core-js: 2.6.12 regenerator-runtime: 0.11.1 - dev: true - /babel-template@6.26.0: - resolution: - { - integrity: sha512-PCOcLFW7/eazGUKIoqH97sO9A2UYMahsn/yRQ7uOk37iutwjq7ODtcTNF+iFDSHNfkctqsLRjLP7URnOx0T1fg==, - } + babel-template@6.26.0: dependencies: babel-runtime: 6.26.0 babel-traverse: 6.26.0 @@ -7560,13 +13398,8 @@ packages: lodash: 4.17.21 transitivePeerDependencies: - supports-color - dev: true - /babel-traverse@6.26.0: - resolution: - { - integrity: sha512-iSxeXx7apsjCHe9c7n8VtRXGzI2Bk1rBSOJgCCjfyXb6v1aCqE1KSEpq/8SXuVN8Ka/Rh1WDTF0MDzkvTA4MIA==, - } + babel-traverse@6.26.0: dependencies: babel-code-frame: 6.26.0 babel-messages: 6.23.0 @@ -7579,54 +13412,23 @@ packages: lodash: 4.17.21 transitivePeerDependencies: - supports-color - dev: true - /babel-types@6.26.0: - resolution: - { - integrity: sha512-zhe3V/26rCWsEZK8kZN+HaQj5yQ1CilTObixFzKW1UWjqG7618Twz6YEsCnjfg5gBcJh02DrpCkS9h98ZqDY+g==, - } + babel-types@6.26.0: dependencies: babel-runtime: 6.26.0 esutils: 2.0.3 lodash: 4.17.21 to-fast-properties: 1.0.3 - dev: true - /babylon@6.18.0: - resolution: - { - integrity: sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==, - } - hasBin: true - dev: true - - /balanced-match@1.0.2: - resolution: - { - integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==, - } - - /balanced-match@2.0.0: - resolution: - { - integrity: sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==, - } - dev: true - - /base64-js@1.5.1: - resolution: - { - integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==, - } - dev: false - - /base@0.11.2: - resolution: - { - integrity: sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==, - } - engines: { node: '>=0.10.0' } + babylon@6.18.0: {} + + balanced-match@1.0.2: {} + + balanced-match@2.0.0: {} + + base64-js@1.5.1: {} + + base@0.11.2: dependencies: cache-base: 1.0.1 class-utils: 0.3.6 @@ -7635,84 +13437,32 @@ packages: isobject: 3.0.1 mixin-deep: 1.3.2 pascalcase: 0.1.1 - dev: false - - /big.js@5.2.2: - resolution: - { - integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==, - } - - /bignumber.js@9.1.2: - resolution: - { - integrity: sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==, - } - requiresBuild: true - dev: false + + baseline-browser-mapping@2.9.11: {} + + big.js@5.2.2: {} + + bignumber.js@9.1.2: optional: true - /binary-extensions@1.13.1: - resolution: - { - integrity: sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==, - } - engines: { node: '>=0.10.0' } - requiresBuild: true - dev: false + binary-extensions@1.13.1: optional: true - /binary-extensions@2.2.0: - resolution: - { - integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==, - } - engines: { node: '>=8' } + binary-extensions@2.2.0: {} - /bindings@1.5.0: - resolution: - { - integrity: sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==, - } - requiresBuild: true + bindings@1.5.0: dependencies: file-uri-to-path: 1.0.0 - dev: false - optional: true - /bluebird@3.7.2: - resolution: - { - integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==, - } - dev: false - - /bn.js@4.12.0: - resolution: - { - integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==, - } - dev: false - - /bn.js@5.2.1: - resolution: - { - integrity: sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==, - } - dev: false - - /boolbase@1.0.0: - resolution: - { - integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==, - } - - /boxen@5.1.2: - resolution: - { - integrity: sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==, - } - engines: { node: '>=10' } + bluebird@3.7.2: {} + + bn.js@4.12.0: {} + + bn.js@5.2.1: {} + + boolbase@1.0.0: {} + + boxen@5.1.2: dependencies: ansi-align: 3.0.1 camelcase: 6.3.0 @@ -7722,31 +13472,26 @@ packages: type-fest: 0.20.2 widest-line: 3.1.0 wrap-ansi: 7.0.0 - dev: false - /brace-expansion@1.1.11: - resolution: - { - integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==, - } + brace-expansion@1.1.11: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - /brace-expansion@2.0.1: - resolution: - { - integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==, - } + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + brace-expansion@2.0.2: dependencies: balanced-match: 1.0.2 - /braces@2.3.2: - resolution: - { - integrity: sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==, - } - engines: { node: '>=0.10.0' } + brace-expansion@2.1.0: + dependencies: + balanced-match: 1.0.2 + optional: true + + braces@2.3.2: dependencies: arr-flatten: 1.1.0 array-unique: 0.3.2 @@ -7760,36 +13505,18 @@ packages: to-regex: 3.0.2 transitivePeerDependencies: - supports-color - dev: false - /braces@3.0.2: - resolution: - { - integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==, - } - engines: { node: '>=8' } + braces@3.0.2: dependencies: fill-range: 7.0.1 - /brorand@1.1.0: - resolution: - { - integrity: sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==, - } - dev: false - - /browser-process-hrtime@1.0.0: - resolution: - { - integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==, - } - dev: true - - /browserify-aes@1.2.0: - resolution: - { - integrity: sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==, - } + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + brorand@1.1.0: {} + + browserify-aes@1.2.0: dependencies: buffer-xor: 1.0.3 cipher-base: 1.0.4 @@ -7797,198 +13524,114 @@ packages: evp_bytestokey: 1.0.3 inherits: 2.0.4 safe-buffer: 5.2.1 - dev: false - /browserify-cipher@1.0.1: - resolution: - { - integrity: sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==, - } + browserify-cipher@1.0.1: dependencies: browserify-aes: 1.2.0 browserify-des: 1.0.2 evp_bytestokey: 1.0.3 - dev: false - /browserify-des@1.0.2: - resolution: - { - integrity: sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==, - } + browserify-des@1.0.2: dependencies: cipher-base: 1.0.4 des.js: 1.1.0 inherits: 2.0.4 safe-buffer: 5.2.1 - dev: false - /browserify-rsa@4.1.0: - resolution: - { - integrity: sha512-AdEER0Hkspgno2aR97SAf6vi0y0k8NuOpGnVH3O99rcA5Q6sh8QxcngtHuJ6uXwnfAXNM4Gn1Gb7/MV1+Ymbog==, - } + browserify-rsa@4.1.0: dependencies: bn.js: 5.2.1 randombytes: 2.1.0 - dev: false - /browserify-sign@4.2.2: - resolution: - { - integrity: sha512-1rudGyeYY42Dk6texmv7c4VcQ0EsvVbLwZkA+AQB7SxvXxmcD93jcHie8bzecJ+ChDlmAm2Qyu0+Ccg5uhZXCg==, - } - engines: { node: '>= 4' } + browserify-sign@4.2.2: dependencies: bn.js: 5.2.1 browserify-rsa: 4.1.0 create-hash: 1.2.0 create-hmac: 1.1.7 - elliptic: 6.5.4 + elliptic: 6.6.0 inherits: 2.0.4 parse-asn1: 5.1.6 readable-stream: 3.6.2 safe-buffer: 5.2.1 - dev: false - /browserify-zlib@0.2.0: - resolution: - { - integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==, - } + browserify-zlib@0.2.0: dependencies: pako: 1.0.11 - dev: false - - /browserslist@4.22.1: - resolution: - { - integrity: sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==, - } - engines: { node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7 } - hasBin: true + + browserslist@4.28.1: dependencies: - caniuse-lite: 1.0.30001541 - electron-to-chromium: 1.4.537 - node-releases: 2.0.13 - update-browserslist-db: 1.0.13(browserslist@4.22.1) + baseline-browser-mapping: 2.9.11 + caniuse-lite: 1.0.30001762 + electron-to-chromium: 1.5.267 + node-releases: 2.0.27 + update-browserslist-db: 1.2.3(browserslist@4.28.1) - /bs-logger@0.2.6: - resolution: - { - integrity: sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==, - } - engines: { node: '>= 6' } + bs-logger@0.2.6: dependencies: fast-json-stable-stringify: 2.1.0 - dev: true - /bser@2.1.1: - resolution: - { - integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==, - } + bser@2.1.1: dependencies: node-int64: 0.4.0 - dev: true - - /buffer-equal-constant-time@1.0.1: - resolution: - { - integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==, - } - requiresBuild: true - dev: false + + buffer-equal-constant-time@1.0.1: optional: true - /buffer-from@1.1.2: - resolution: - { - integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==, - } - - /buffer-json@2.0.0: - resolution: - { - integrity: sha512-+jjPFVqyfF1esi9fvfUs3NqM0pH1ziZ36VP4hmA/y/Ssfo/5w5xHKfTw9BwQjoJ1w/oVtpLomqwUHKdefGyuHw==, - } - dev: false - - /buffer-xor@1.0.3: - resolution: - { - integrity: sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==, - } - dev: false - - /buffer@4.9.2: - resolution: - { - integrity: sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==, - } + buffer-from@1.1.2: {} + + buffer-json@2.0.0: {} + + buffer-xor@1.0.3: {} + + buffer@4.9.2: dependencies: base64-js: 1.5.1 ieee754: 1.2.1 isarray: 1.0.0 - dev: false - - /builtin-modules@3.3.0: - resolution: - { - integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==, - } - engines: { node: '>=6' } - dev: true - - /builtin-status-codes@3.0.0: - resolution: - { - integrity: sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==, - } - dev: false - - /builtins@5.0.1: - resolution: - { - integrity: sha512-qwVpFEHNfhYJIzNRBvd2C1kyo6jz3ZSMPyyuR47OPdiKWlbYnZNyDWuyR175qDnAJLiCo5fBBqPb3RiXgWlkOQ==, - } + + builtin-modules@3.3.0: {} + + builtin-status-codes@3.0.0: {} + + builtins@5.0.1: dependencies: - semver: 7.5.4 - dev: true - - /bytes@3.0.0: - resolution: - { - integrity: sha512-pMhOfFDPiv9t5jjIXkHosWmkSyQbvsgEVNkz0ERHbuLh2T/7j4Mqqpz523Fe8MVY89KC6Sh/QfS2sM+SjgFDcw==, - } - engines: { node: '>= 0.8' } - dev: false - - /c12@1.4.2: - resolution: - { - integrity: sha512-3IP/MuamSVRVw8W8+CHWAz9gKN4gd+voF2zm/Ln6D25C2RhytEZ1ABbC8MjKr4BR9rhoV1JQ7jJA158LDiTkLg==, - } + semver: 7.7.3 + + bytes@3.0.0: {} + + c12@1.11.1: dependencies: - chokidar: 3.5.3 - defu: 6.1.2 - dotenv: 16.3.1 + chokidar: 3.6.0 + confbox: 0.1.7 + defu: 6.1.4 + dotenv: 16.6.1 + giget: 1.2.3 + jiti: 1.21.6 + mlly: 1.7.1 + ohash: 1.1.3 + pathe: 1.1.2 + perfect-debounce: 1.0.0 + pkg-types: 1.1.2 + rc9: 2.1.2 + + c12@1.4.2: + dependencies: + chokidar: 3.6.0 + defu: 6.1.4 + dotenv: 16.6.1 giget: 1.1.2 - jiti: 1.20.0 - mlly: 1.4.2 + jiti: 1.21.6 + mlly: 1.7.1 ohash: 1.1.3 - pathe: 1.1.1 + pathe: 1.1.2 perfect-debounce: 1.0.0 - pkg-types: 1.0.3 + pkg-types: 1.1.2 rc9: 2.1.1 transitivePeerDependencies: - supports-color - dev: true - /cacache@12.0.4: - resolution: - { - integrity: sha512-a0tMB40oefvuInr4Cwb3GerbL9xTj1D5yg0T5xrjGCGyfvbxseIXX7BAO/u/hIXdafzOI5JC3wDwHyf24buOAQ==, - } + cacache@12.0.4: dependencies: bluebird: 3.7.2 chownr: 1.1.4 @@ -8005,14 +13648,8 @@ packages: ssri: 6.0.2 unique-filename: 1.1.1 y18n: 4.0.3 - dev: false - /cacache@15.3.0: - resolution: - { - integrity: sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==, - } - engines: { node: '>= 10' } + cacache@15.3.0: dependencies: '@npmcli/fs': 1.1.1 '@npmcli/move-file': 1.1.2 @@ -8034,14 +13671,8 @@ packages: unique-filename: 1.1.1 transitivePeerDependencies: - bluebird - dev: false - /cache-base@1.0.1: - resolution: - { - integrity: sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==, - } - engines: { node: '>=0.10.0' } + cache-base@1.0.1: dependencies: collection-visit: 1.0.0 component-emitter: 1.3.0 @@ -8052,16 +13683,8 @@ packages: to-object-path: 0.3.0 union-value: 1.0.1 unset-value: 1.0.0 - dev: false - /cache-loader@4.1.0(webpack@4.47.0): - resolution: - { - integrity: sha512-ftOayxve0PwKzBF/GLsZNC9fJBXl8lkZE3TOsjkboHfVHVkL39iUEs1FO07A33mizmci5Dudt38UZrrYXDtbhw==, - } - engines: { node: '>= 8.9.0' } - peerDependencies: - webpack: ^4.0.0 + cache-loader@4.1.0(webpack@4.47.0): dependencies: buffer-json: 2.0.0 find-cache-dir: 3.3.2 @@ -8070,203 +13693,83 @@ packages: neo-async: 2.6.2 schema-utils: 2.7.1 webpack: 4.47.0 - dev: false - /call-bind@1.0.2: - resolution: - { - integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==, - } + call-bind-apply-helpers@1.0.2: dependencies: - function-bind: 1.1.1 - get-intrinsic: 1.2.1 + es-errors: 1.3.0 + function-bind: 1.1.2 - /callsite@1.0.0: - resolution: - { - integrity: sha512-0vdNRFXn5q+dtOqjfFtmtlI9N2eVZ7LMyEV2iKC5mEEFvSg/69Ml6b/WU2qF8W1nLRa0wiSrDT3Y5jOHZCwKPQ==, - } - dev: true - - /callsites@3.1.0: - resolution: - { - integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==, - } - engines: { node: '>=6' } - - /camel-case@3.0.0: - resolution: - { - integrity: sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w==, - } - dependencies: - no-case: 2.3.2 - upper-case: 1.1.3 - dev: false - - /camel-case@4.1.2: - resolution: - { - integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==, - } + call-bind@1.0.2: dependencies: - pascal-case: 3.1.2 - tslib: 2.6.2 - dev: false + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + + callsite@1.0.0: {} + + callsites@3.1.0: {} - /camelcase-keys@6.2.2: - resolution: - { - integrity: sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==, - } - engines: { node: '>=8' } + camel-case@4.1.2: dependencies: - camelcase: 5.3.1 - map-obj: 4.3.0 - quick-lru: 4.0.1 - dev: true + pascal-case: 3.1.2 + tslib: 2.6.2 - /camelcase-keys@7.0.2: - resolution: - { - integrity: sha512-Rjs1H+A9R+Ig+4E/9oyB66UC5Mj9Xq3N//vcLf2WzgdTi/3gUu3Z9KoqmlrEG4VuuLK8wJHofxzdQXz/knhiYg==, - } - engines: { node: '>=12' } + camelcase-keys@7.0.2: dependencies: camelcase: 6.3.0 map-obj: 4.3.0 quick-lru: 5.1.1 type-fest: 1.4.0 - dev: true - - /camelcase@5.3.1: - resolution: - { - integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==, - } - engines: { node: '>=6' } - - /camelcase@6.3.0: - resolution: - { - integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==, - } - engines: { node: '>=10' } - - /caniuse-api@3.0.0: - resolution: - { - integrity: sha512-bsTwuIg/BZZK/vreVTYYbSWoe2F+71P7K5QGEX+pT250DZbfU1MQ5prOKpPR+LL6uWKK3KMwMCAS74QB3Um1uw==, - } - dependencies: - browserslist: 4.22.1 - caniuse-lite: 1.0.30001561 + + camelcase@5.3.1: {} + + camelcase@6.3.0: {} + + caniuse-api@3.0.0: + dependencies: + browserslist: 4.28.1 + caniuse-lite: 1.0.30001762 lodash.memoize: 4.1.2 lodash.uniq: 4.5.0 - dev: false - - /caniuse-lite@1.0.30001541: - resolution: - { - integrity: sha512-bLOsqxDgTqUBkzxbNlSBt8annkDpQB9NdzdTbO2ooJ+eC/IQcvDspDc058g84ejCelF7vHUx57KIOjEecOHXaw==, - } - - /caniuse-lite@1.0.30001561: - resolution: - { - integrity: sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw==, - } - dev: false - - /chalk@1.1.3: - resolution: - { - integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==, - } - engines: { node: '>=0.10.0' } + + caniuse-lite@1.0.30001639: {} + + caniuse-lite@1.0.30001762: {} + + chalk@1.1.3: dependencies: ansi-styles: 2.2.1 escape-string-regexp: 1.0.5 has-ansi: 2.0.0 strip-ansi: 3.0.1 supports-color: 2.0.0 - dev: true - /chalk@2.4.2: - resolution: - { - integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==, - } - engines: { node: '>=4' } + chalk@2.4.2: dependencies: ansi-styles: 3.2.1 escape-string-regexp: 1.0.5 supports-color: 5.5.0 - /chalk@4.1.2: - resolution: - { - integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==, - } - engines: { node: '>=10' } + chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 - /chalk@5.3.0: - resolution: - { - integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==, - } - engines: { node: ^12.17.0 || ^14.13 || >=16.0.0 } - dev: true - - /char-regex@1.0.2: - resolution: - { - integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==, - } - engines: { node: '>=10' } - dev: true - - /chardet@0.7.0: - resolution: - { - integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==, - } - dev: false - - /chart.js@4.4.0: - resolution: - { - integrity: sha512-vQEj6d+z0dcsKLlQvbKIMYFHd3t8W/7L2vfJIbYcfyPcRx92CsHqECpueN8qVGNlKyDcr5wBrYAYKnfu/9Q1hQ==, - } - engines: { pnpm: '>=7' } - dependencies: - '@kurkle/color': 0.3.2 - dev: false - - /chartjs-adapter-moment@1.0.1(chart.js@4.4.0)(moment@2.29.4): - resolution: - { - integrity: sha512-Uz+nTX/GxocuqXpGylxK19YG4R3OSVf8326D+HwSTsNw1LgzyIGRo+Qujwro1wy6X+soNSnfj5t2vZ+r6EaDmA==, - } - peerDependencies: - chart.js: '>=3.0.0' - moment: ^2.10.2 + chalk@5.5.0: {} + + char-regex@1.0.2: {} + + chardet@0.7.0: {} + + chart.js@4.5.1: dependencies: - chart.js: 4.4.0 - moment: 2.29.4 - dev: false + '@kurkle/color': 0.3.4 - /chokidar@2.1.8: - resolution: - { - integrity: sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==, - } - deprecated: Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies - requiresBuild: true + chartjs-adapter-moment@1.0.1(chart.js@4.5.1)(moment@2.30.1): + dependencies: + chart.js: 4.5.1 + moment: 2.30.1 + + chokidar@2.1.8: dependencies: anymatch: 2.0.0 async-each: 1.0.6 @@ -8283,15 +13786,9 @@ packages: fsevents: 1.2.13 transitivePeerDependencies: - supports-color - dev: false optional: true - /chokidar@3.5.3: - resolution: - { - integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==, - } - engines: { node: '>= 8.10.0' } + chokidar@3.5.3: dependencies: anymatch: 3.1.3 braces: 3.0.2 @@ -8303,669 +13800,243 @@ packages: optionalDependencies: fsevents: 2.3.3 - /chownr@1.1.4: - resolution: - { - integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==, - } - dev: false - - /chownr@2.0.0: - resolution: - { - integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==, - } - engines: { node: '>=10' } - - /chrome-trace-event@1.0.3: - resolution: - { - integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==, - } - engines: { node: '>=6.0' } - - /ci-info@3.8.0: - resolution: - { - integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==, - } - engines: { node: '>=8' } - - /cipher-base@1.0.4: - resolution: - { - integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==, - } + chokidar@3.6.0: dependencies: - inherits: 2.0.4 - safe-buffer: 5.2.1 - dev: false - - /cjs-module-lexer@1.2.3: - resolution: - { - integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==, - } - dev: true - - /class-utils@0.3.6: - resolution: - { - integrity: sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==, - } - engines: { node: '>=0.10.0' } - dependencies: - arr-union: 3.1.0 - define-property: 0.2.5 - isobject: 3.0.1 - static-extend: 0.1.2 - dev: false + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 - /clean-css@4.2.4: - resolution: - { - integrity: sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==, - } - engines: { node: '>= 4.0' } - dependencies: - source-map: 0.6.1 - dev: false + chownr@1.1.4: {} - /clean-regexp@1.0.0: - resolution: - { - integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==, - } - engines: { node: '>=4' } - dependencies: - escape-string-regexp: 1.0.5 - dev: true - - /clean-stack@2.2.0: - resolution: - { - integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==, - } - engines: { node: '>=6' } - dev: false - - /cli-boxes@2.2.1: - resolution: - { - integrity: sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==, - } - engines: { node: '>=6' } - dev: false - - /cli-cursor@3.1.0: - resolution: - { - integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==, - } - engines: { node: '>=8' } - dependencies: - restore-cursor: 3.1.0 - dev: false + chownr@2.0.0: {} - /cli-cursor@4.0.0: - resolution: - { - integrity: sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==, - } - engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } - dependencies: - restore-cursor: 4.0.0 - dev: true + chrome-trace-event@1.0.4: {} - /cli-truncate@3.1.0: - resolution: - { - integrity: sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==, - } - engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } - dependencies: - slice-ansi: 5.0.0 - string-width: 5.1.2 - dev: true - - /cli-width@3.0.0: - resolution: - { - integrity: sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==, - } - engines: { node: '>= 10' } - dev: false - - /cliui@7.0.4: - resolution: - { - integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==, - } - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 + ci-info@3.8.0: {} - /cliui@8.0.1: - resolution: - { - integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==, - } - engines: { node: '>=12' } - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 + ci-info@3.9.0: {} - /clone@2.1.2: - resolution: - { - integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==, - } - engines: { node: '>=0.8' } - dev: true - - /co@4.6.0: - resolution: - { - integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==, - } - engines: { iojs: '>= 1.0.0', node: '>= 0.12.0' } - dev: true - - /collect-v8-coverage@1.0.2: - resolution: - { - integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==, - } - dev: true - - /collection-visit@1.0.0: - resolution: - { - integrity: sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==, - } - engines: { node: '>=0.10.0' } - dependencies: - map-visit: 1.0.0 - object-visit: 1.0.1 - dev: false + ci-info@4.3.0: {} - /color-convert@1.9.3: - resolution: - { - integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==, - } + cipher-base@1.0.4: dependencies: - color-name: 1.1.3 + inherits: 2.0.4 + safe-buffer: 5.2.1 - /color-convert@2.0.1: - resolution: - { - integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==, - } - engines: { node: '>=7.0.0' } + citty@0.1.6: dependencies: - color-name: 1.1.4 + consola: 3.2.3 + + cjs-module-lexer@2.1.0: {} - /color-name@1.1.3: - resolution: - { - integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==, - } - - /color-name@1.1.4: - resolution: - { - integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==, - } - - /colord@2.9.3: - resolution: - { - integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==, - } - - /colorette@2.0.20: - resolution: - { - integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==, - } - - /combined-stream@1.0.8: - resolution: - { - integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==, - } - engines: { node: '>= 0.8' } + class-utils@0.3.6: dependencies: - delayed-stream: 1.0.0 - dev: true - - /commander@10.0.1: - resolution: - { - integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==, - } - engines: { node: '>=14' } - dev: true - - /commander@11.0.0: - resolution: - { - integrity: sha512-9HMlXtt/BNoYr8ooyjjNRdIilOTkVJXB+GhxMTtOKwk0R4j4lS4NpjuqmRxroBfnfTSHQIHQB7wryHhXarNjmQ==, - } - engines: { node: '>=16' } - dev: true - - /commander@2.20.3: - resolution: - { - integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==, - } - - /commander@4.1.1: - resolution: - { - integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==, - } - engines: { node: '>= 6' } - dev: false - - /commander@7.2.0: - resolution: - { - integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==, - } - engines: { node: '>= 10' } - dev: false - - /commondir@1.0.1: - resolution: - { - integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==, - } - dev: false - - /compare-func@2.0.0: - resolution: - { - integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==, - } + arr-union: 3.1.0 + define-property: 0.2.5 + isobject: 3.0.1 + static-extend: 0.1.2 + + clean-css@4.2.4: dependencies: - array-ify: 1.0.0 - dot-prop: 5.3.0 - dev: true - - /component-emitter@1.3.0: - resolution: - { - integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==, - } - dev: false - - /compressible@2.0.18: - resolution: - { - integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==, - } - engines: { node: '>= 0.6' } + source-map: 0.6.1 + + clean-css@5.3.3: dependencies: - mime-db: 1.52.0 - dev: false + source-map: 0.6.1 - /compression@1.7.4: - resolution: - { - integrity: sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==, - } - engines: { node: '>= 0.8.0' } + clean-regexp@1.0.0: dependencies: - accepts: 1.3.8 - bytes: 3.0.0 - compressible: 2.0.18 - debug: 2.6.9 - on-headers: 1.0.2 - safe-buffer: 5.1.2 - vary: 1.1.2 - transitivePeerDependencies: - - supports-color - dev: false + escape-string-regexp: 1.0.5 - /concat-map@0.0.1: - resolution: - { - integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==, - } + clean-stack@2.2.0: {} - /concat-stream@1.6.2: - resolution: - { - integrity: sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==, - } - engines: { '0': node >= 0.8 } + cli-boxes@2.2.1: {} + + cli-cursor@3.1.0: dependencies: - buffer-from: 1.1.2 - inherits: 2.0.4 - readable-stream: 2.3.8 - typedarray: 0.0.6 - dev: false + restore-cursor: 3.1.0 - /condense-newlines@0.2.1: - resolution: - { - integrity: sha512-P7X+QL9Hb9B/c8HI5BFFKmjgBu2XpQuF98WZ9XkO+dBGgk5XgwiQz7o1SmpglNWId3581UcS0SFAWfoIhMHPfg==, - } - engines: { node: '>=0.10.0' } + cli-cursor@5.0.0: dependencies: - extend-shallow: 2.0.1 - is-whitespace: 0.3.0 - kind-of: 3.2.2 - dev: true + restore-cursor: 5.1.0 - /config-chain@1.1.13: - resolution: - { - integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==, - } + cli-truncate@4.0.0: dependencies: - ini: 1.3.8 - proto-list: 1.2.4 - dev: true + slice-ansi: 5.0.0 + string-width: 7.2.0 - /configstore@5.0.1: - resolution: - { - integrity: sha512-aMKprgk5YhBNyH25hj8wGt2+D52Sw1DRRIzqBwLp2Ya9mFmY8KPvvtvmna8SxVR9JMZ4kzMD68N22vlaRpkeFA==, - } - engines: { node: '>=8' } - requiresBuild: true + cli-width@3.0.0: {} + + cliui@6.0.0: dependencies: - dot-prop: 5.3.0 - graceful-fs: 4.2.11 - make-dir: 3.1.0 - unique-string: 2.0.0 - write-file-atomic: 3.0.3 - xdg-basedir: 4.0.0 - dev: false - optional: true + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 - /connect@3.7.0: - resolution: - { - integrity: sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==, - } - engines: { node: '>= 0.10.0' } + cliui@7.0.4: dependencies: - debug: 2.6.9 - finalhandler: 1.1.2 - parseurl: 1.3.3 - utils-merge: 1.0.1 - transitivePeerDependencies: - - supports-color - dev: false - - /consola@2.15.3: - resolution: - { - integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==, - } - - /consola@3.2.3: - resolution: - { - integrity: sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ==, - } - engines: { node: ^14.18.0 || >=16.10.0 } - - /console-browserify@1.2.0: - resolution: - { - integrity: sha512-ZMkYO/LkF17QvCPqM0gxw8yUzigAOZOSWSHg91FH6orS7vcEj5dVZTidN2fQ14yBSdg97RqhSNwLUXInd52OTA==, - } - dev: false - - /consolidate@0.15.1(babel-core@7.0.0-bridge.0)(lodash@4.17.21): - resolution: - { - integrity: sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==, - } - engines: { node: '>= 0.10.0' } - deprecated: Please upgrade to consolidate v1.0.0+ as it has been modernized with several long-awaited fixes implemented. Maintenance is supported by Forward Email at https://forwardemail.net ; follow/watch https://github.com/ladjs/consolidate for updates and release changelog - peerDependencies: - arc-templates: ^0.5.3 - atpl: '>=0.7.6' - babel-core: ^6.26.3 - bracket-template: ^1.1.5 - coffee-script: ^1.12.7 - dot: ^1.1.3 - dust: ^0.3.0 - dustjs-helpers: ^1.7.4 - dustjs-linkedin: ^2.7.5 - eco: ^1.1.0-rc-3 - ect: ^0.5.9 - ejs: ^3.1.5 - haml-coffee: ^1.14.1 - hamlet: ^0.3.3 - hamljs: ^0.6.2 - handlebars: ^4.7.6 - hogan.js: ^3.0.2 - htmling: ^0.0.8 - jade: ^1.11.0 - jazz: ^0.0.18 - jqtpl: ~1.1.0 - just: ^0.1.8 - liquid-node: ^3.0.1 - liquor: ^0.0.5 - lodash: ^4.17.20 - marko: ^3.14.4 - mote: ^0.2.0 - mustache: ^3.0.0 - nunjucks: ^3.2.2 - plates: ~0.4.11 - pug: ^3.0.0 - qejs: ^3.0.5 - ractive: ^1.3.12 - razor-tmpl: ^1.3.1 - react: ^16.13.1 - react-dom: ^16.13.1 - slm: ^2.0.0 - squirrelly: ^5.1.0 - swig: ^1.4.2 - swig-templates: ^2.0.3 - teacup: ^2.0.0 - templayed: '>=0.2.3' - then-jade: '*' - then-pug: '*' - tinyliquid: ^0.2.34 - toffee: ^0.3.6 - twig: ^1.15.2 - twing: ^5.0.2 - underscore: ^1.11.0 - vash: ^0.13.0 - velocityjs: ^2.0.1 - walrus: ^0.10.1 - whiskers: ^0.4.0 - peerDependenciesMeta: - arc-templates: - optional: true - atpl: - optional: true - babel-core: - optional: true - bracket-template: - optional: true - coffee-script: - optional: true - dot: - optional: true - dust: - optional: true - dustjs-helpers: - optional: true - dustjs-linkedin: - optional: true - eco: - optional: true - ect: - optional: true - ejs: - optional: true - haml-coffee: - optional: true - hamlet: - optional: true - hamljs: - optional: true - handlebars: - optional: true - hogan.js: - optional: true - htmling: - optional: true - jade: - optional: true - jazz: - optional: true - jqtpl: - optional: true - just: - optional: true - liquid-node: - optional: true - liquor: - optional: true - lodash: - optional: true - marko: - optional: true - mote: - optional: true - mustache: - optional: true - nunjucks: - optional: true - plates: - optional: true - pug: - optional: true - qejs: - optional: true - ractive: - optional: true - razor-tmpl: - optional: true - react: - optional: true - react-dom: - optional: true - slm: - optional: true - squirrelly: - optional: true - swig: - optional: true - swig-templates: - optional: true - teacup: - optional: true - templayed: - optional: true - then-jade: - optional: true - then-pug: - optional: true - tinyliquid: - optional: true - toffee: - optional: true - twig: - optional: true - twing: - optional: true - underscore: - optional: true - vash: - optional: true - velocityjs: - optional: true - walrus: - optional: true - whiskers: - optional: true + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + optional: true + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + clone@2.1.2: {} + + co@4.6.0: {} + + collect-v8-coverage@1.0.2: {} + + collection-visit@1.0.0: + dependencies: + map-visit: 1.0.0 + object-visit: 1.0.1 + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.3: {} + + color-name@1.1.4: {} + + colord@2.9.3: {} + + colorette@2.0.20: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + commander@10.0.1: {} + + commander@14.0.0: {} + + commander@2.20.3: {} + + commander@4.1.1: {} + + commander@7.2.0: {} + + commondir@1.0.1: {} + + compare-func@2.0.0: + dependencies: + array-ify: 1.0.0 + dot-prop: 5.3.0 + + compatx@0.1.8: {} + + component-emitter@1.3.0: {} + + compressible@2.0.18: + dependencies: + mime-db: 1.54.0 + + compression@1.7.4: + dependencies: + accepts: 1.3.8 + bytes: 3.0.0 + compressible: 2.0.18 + debug: 2.6.9 + on-headers: 1.0.2 + safe-buffer: 5.1.2 + vary: 1.1.2 + transitivePeerDependencies: + - supports-color + + concat-map@0.0.1: {} + + concat-stream@1.6.2: + dependencies: + buffer-from: 1.1.2 + inherits: 2.0.4 + readable-stream: 2.3.8 + typedarray: 0.0.6 + + condense-newlines@0.2.1: + dependencies: + extend-shallow: 2.0.1 + is-whitespace: 0.3.0 + kind-of: 3.2.2 + + confbox@0.1.7: {} + + confbox@0.1.8: {} + + config-chain@1.1.13: + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + + configstore@5.0.1: + dependencies: + dot-prop: 5.3.0 + graceful-fs: 4.2.11 + make-dir: 3.1.0 + unique-string: 2.0.0 + write-file-atomic: 3.0.3 + xdg-basedir: 4.0.0 + optional: true + + connect@3.7.0: + dependencies: + debug: 2.6.9 + finalhandler: 1.1.2 + parseurl: 1.3.3 + utils-merge: 1.0.1 + transitivePeerDependencies: + - supports-color + + consola@2.15.3: {} + + consola@3.2.3: {} + + console-browserify@1.2.0: {} + + consolidate@0.15.1(babel-core@7.0.0-bridge.0(@babel/core@7.28.4))(ejs@3.1.10)(handlebars@4.7.8)(lodash@4.17.21): dependencies: - babel-core: 7.0.0-bridge.0(@babel/core@7.23.2) bluebird: 3.7.2 + optionalDependencies: + babel-core: 7.0.0-bridge.0(@babel/core@7.28.4) + ejs: 3.1.10 + handlebars: 4.7.8 lodash: 4.17.21 - dev: false - - /constants-browserify@1.0.0: - resolution: - { - integrity: sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==, - } - dev: false - - /conventional-changelog-angular@6.0.0: - resolution: - { - integrity: sha512-6qLgrBF4gueoC7AFVHu51nHL9pF9FRjXrH+ceVf7WmAfH3gs+gEYOkvxhjMPjZu57I4AGUGoNTY8V7Hrgf1uqg==, - } - engines: { node: '>=14' } + + constants-browserify@1.0.0: {} + + conventional-changelog-angular@8.1.0: dependencies: compare-func: 2.0.0 - dev: true - /conventional-changelog-conventionalcommits@7.0.2: - resolution: - { - integrity: sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==, - } - engines: { node: '>=16' } + conventional-changelog-conventionalcommits@9.1.0: dependencies: compare-func: 2.0.0 - dev: true - - /conventional-commits-parser@4.0.0: - resolution: - { - integrity: sha512-WRv5j1FsVM5FISJkoYMR6tPk07fkKT0UodruX4je86V4owk451yjXAKzKAPOs9l7y59E2viHUS9eQ+dfUA9NSg==, - } - engines: { node: '>=14' } - hasBin: true + + conventional-commits-parser@6.2.1: dependencies: - JSONStream: 1.3.5 - is-text-path: 1.0.1 - meow: 8.1.2 - split2: 3.2.2 - dev: true - - /convert-source-map@1.9.0: - resolution: - { - integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==, - } - dev: true - - /convert-source-map@2.0.0: - resolution: - { - integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==, - } - - /cookie@0.3.1: - resolution: - { - integrity: sha512-+IJOX0OqlHCszo2mBUq+SrEbCj6w7Kpffqx60zYbPTFaO4+yYgRjHwcZNpWvaTylDHaV7PPmBHzSecZiMhtPgw==, - } - engines: { node: '>= 0.6' } - dev: false - - /copy-concurrently@1.0.5: - resolution: - { - integrity: sha512-f2domd9fsVDFtaFcbaRZuYXwtdmnzqbADSwhSWYxYB/Q8zsdUUFMXVRwXGDMWmbEzAn1kdRrtI1T/KTFOL4X2A==, - } + meow: 13.2.0 + + convert-source-map@2.0.0: {} + + cookie@0.3.1: {} + + copy-concurrently@1.0.5: dependencies: aproba: 1.2.0 fs-write-stream-atomic: 1.0.10 @@ -8973,154 +14044,76 @@ packages: mkdirp: 0.5.6 rimraf: 2.7.1 run-queue: 1.0.3 - dev: false - - /copy-descriptor@0.1.1: - resolution: - { - integrity: sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==, - } - engines: { node: '>=0.10.0' } - dev: false - - /core-js-compat@3.33.2: - resolution: - { - integrity: sha512-axfo+wxFVxnqf8RvxTzoAlzW4gRoacrHeoFlc9n0x50+7BEyZL/Rt3hicaED1/CEd7I6tPCPVUYcJwCMO5XUYw==, - } - dependencies: - browserslist: 4.22.1 - dev: false - - /core-js@2.6.12: - resolution: - { - integrity: sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==, - } - deprecated: core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js. - requiresBuild: true - dev: true - - /core-js@3.33.2: - resolution: - { - integrity: sha512-XeBzWI6QL3nJQiHmdzbAOiMYqjrb7hwU7A39Qhvd/POSa/t9E1AeZyEZx3fNvp/vtM8zXwhoL0FsiS0hD0pruQ==, - } - requiresBuild: true - dev: false - - /core-util-is@1.0.3: - resolution: - { - integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==, - } - - /cosmiconfig-typescript-loader@4.4.0(@types/node@20.5.1)(cosmiconfig@8.3.6)(ts-node@10.9.1)(typescript@5.2.2): - resolution: - { - integrity: sha512-BabizFdC3wBHhbI4kJh0VkQP9GkBfoHPydD0COMce1nJ1kJAB3F2TmJ/I7diULBKtmEWSwEbuN/KDtgnmUUVmw==, - } - engines: { node: '>=v14.21.3' } - peerDependencies: - '@types/node': '*' - cosmiconfig: '>=7' - ts-node: '>=10' - typescript: '>=4' + + copy-descriptor@0.1.1: {} + + core-js-compat@3.37.1: dependencies: - '@types/node': 20.5.1 - cosmiconfig: 8.3.6(typescript@4.9.5) - ts-node: 10.9.1(@types/node@20.5.1)(typescript@4.9.5) - typescript: 5.2.2 - dev: true + browserslist: 4.28.1 + + core-js@2.6.12: {} + + core-js@3.48.0: {} + + core-util-is@1.0.3: {} - /cosmiconfig@6.0.0: - resolution: - { - integrity: sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==, - } - engines: { node: '>=8' } + cosmiconfig-typescript-loader@6.2.0(@types/node@25.1.0)(cosmiconfig@9.0.0(typescript@4.9.5))(typescript@4.9.5): + dependencies: + '@types/node': 25.1.0 + cosmiconfig: 9.0.0(typescript@4.9.5) + jiti: 2.6.1 + typescript: 4.9.5 + + cosmiconfig@6.0.0: dependencies: '@types/parse-json': 4.0.0 import-fresh: 3.3.0 parse-json: 5.2.0 path-type: 4.0.0 yaml: 1.10.2 - dev: true - /cosmiconfig@7.1.0: - resolution: - { - integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==, - } - engines: { node: '>=10' } + cosmiconfig@7.1.0: dependencies: '@types/parse-json': 4.0.0 - import-fresh: 3.3.0 + import-fresh: 3.3.1 parse-json: 5.2.0 path-type: 4.0.0 yaml: 1.10.2 - dev: false - /cosmiconfig@8.3.6(typescript@4.9.5): - resolution: - { - integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==, - } - engines: { node: '>=14' } - peerDependencies: - typescript: '>=4.9.5' - peerDependenciesMeta: - typescript: - optional: true + cosmiconfig@8.3.6(typescript@4.9.5): dependencies: import-fresh: 3.3.0 js-yaml: 4.1.0 parse-json: 5.2.0 path-type: 4.0.0 + optionalDependencies: typescript: 4.9.5 - dev: true - /crc@4.3.2: - resolution: - { - integrity: sha512-uGDHf4KLLh2zsHa8D8hIQ1H/HtFQhyHrc0uhHBcoKGol/Xnb+MPYfUMw7cvON6ze/GUESTudKayDcJC5HnJv1A==, - } - engines: { node: '>=12' } - peerDependencies: - buffer: '>=6.0.3' - peerDependenciesMeta: - buffer: - optional: true - dev: false + cosmiconfig@9.0.0(typescript@4.9.5): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + parse-json: 5.2.0 + optionalDependencies: + typescript: 4.9.5 + + crc@4.3.2: {} - /create-ecdh@4.0.4: - resolution: - { - integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==, - } + create-ecdh@4.0.4: dependencies: bn.js: 4.12.0 - elliptic: 6.5.4 - dev: false + elliptic: 6.6.0 - /create-hash@1.2.0: - resolution: - { - integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==, - } + create-hash@1.2.0: dependencies: cipher-base: 1.0.4 inherits: 2.0.4 md5.js: 1.3.5 ripemd160: 2.0.2 sha.js: 2.4.11 - dev: false - /create-hmac@1.1.7: - resolution: - { - integrity: sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==, - } + create-hmac@1.1.7: dependencies: cipher-base: 1.0.4 create-hash: 1.2.0 @@ -9128,30 +14121,16 @@ packages: ripemd160: 2.0.2 safe-buffer: 5.2.1 sha.js: 2.4.11 - dev: false - /create-require@1.1.1: - resolution: - { - integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==, - } + create-require@1.1.1: {} - /cross-spawn@7.0.3: - resolution: - { - integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==, - } - engines: { node: '>= 8' } + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - /crypto-browserify@3.12.0: - resolution: - { - integrity: sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==, - } + crypto-browserify@3.12.0: dependencies: browserify-cipher: 1.0.1 browserify-sign: 4.2.2 @@ -9164,965 +14143,460 @@ packages: public-encrypt: 4.0.3 randombytes: 2.1.0 randomfill: 1.0.4 - dev: false - - /crypto-random-string@2.0.0: - resolution: - { - integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==, - } - engines: { node: '>=8' } - requiresBuild: true - dev: false + + crypto-random-string@2.0.0: optional: true - /css-blank-pseudo@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-VbfLlOWO7sBHBTn6pwDQzc07Z0SDydgDBfNfCE0nvrehdBNv9RKsuupIRa/qal0+fBZhAALyQDPMKz5lnvcchw==, - } - engines: { node: ^14 || ^16 || >=18 } - peerDependencies: - postcss: ^8.4 + css-blank-pseudo@6.0.2(postcss@8.5.6): dependencies: - postcss: 8.4.31 - postcss-selector-parser: 6.0.13 - dev: false + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 - /css-declaration-sorter@6.4.1(postcss@8.4.31): - resolution: - { - integrity: sha512-rtdthzxKuyq6IzqX6jEcIzQF/YqccluefyCYheovBOLhFT/drQA9zj/UbRAa9J7C0o6EG6u3E6g+vKkay7/k3g==, - } - engines: { node: ^10 || ^12 || >=14 } - peerDependencies: - postcss: ^8.0.9 + css-declaration-sorter@6.4.1(postcss@8.5.6): dependencies: - postcss: 8.4.31 - dev: false - - /css-functions-list@3.2.1: - resolution: - { - integrity: sha512-Nj5YcaGgBtuUmn1D7oHqPW0c9iui7xsTsj5lIX8ZgevdfhmjFfKB3r8moHJtNJnctnYXJyYX5I1pp90HM4TPgQ==, - } - engines: { node: '>=12 || >=16' } - dev: true - - /css-has-pseudo@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-X+r+JBuoO37FBOWVNhVJhxtSBUFHgHbrcc0CjFT28JEdOw1qaDwABv/uunyodUuSy2hMPe9j/HjssxSlvUmKjg==, - } - engines: { node: ^14 || ^16 || >=18 } - peerDependencies: - postcss: ^8.4 + postcss: 8.5.6 + + css-declaration-sorter@7.2.0(postcss@8.5.6): dependencies: - '@csstools/selector-specificity': 3.0.0(postcss-selector-parser@6.0.13) - postcss: 8.4.31 - postcss-selector-parser: 6.0.13 + postcss: 8.5.6 + + css-functions-list@3.2.1: {} + + css-has-pseudo@6.0.5(postcss@8.5.6): + dependencies: + '@csstools/selector-specificity': 3.1.1(postcss-selector-parser@6.1.2) + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 postcss-value-parser: 4.2.0 - dev: false - /css-loader@5.2.7(webpack@4.47.0): - resolution: - { - integrity: sha512-Q7mOvpBNBG7YrVGMxRxcBJZFL75o+cH2abNASdibkj/fffYD8qWbInZrD0S9ccI6vZclF3DsHE7njGlLtaHbhg==, - } - engines: { node: '>= 10.13.0' } - peerDependencies: - webpack: ^4.27.0 || ^5.0.0 + css-loader@5.2.7(webpack@4.47.0): dependencies: - icss-utils: 5.1.0(postcss@8.4.31) + icss-utils: 5.1.0(postcss@8.5.6) loader-utils: 2.0.4 - postcss: 8.4.31 - postcss-modules-extract-imports: 3.0.0(postcss@8.4.31) - postcss-modules-local-by-default: 4.0.3(postcss@8.4.31) - postcss-modules-scope: 3.0.0(postcss@8.4.31) - postcss-modules-values: 4.0.0(postcss@8.4.31) + postcss: 8.5.6 + postcss-modules-extract-imports: 3.0.0(postcss@8.5.6) + postcss-modules-local-by-default: 4.0.3(postcss@8.5.6) + postcss-modules-scope: 3.0.0(postcss@8.5.6) + postcss-modules-values: 4.0.0(postcss@8.5.6) postcss-value-parser: 4.2.0 schema-utils: 3.3.0 - semver: 7.5.4 + semver: 7.7.3 webpack: 4.47.0 - dev: false - /css-prefers-color-scheme@9.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-03QGAk/FXIRseDdLb7XAiu6gidQ0Nd8945xuM7VFVPpc6goJsG9uIO8xQjTxwbPdPIIV4o4AJoOJyt8gwDl67g==, - } - engines: { node: ^14 || ^16 || >=18 } - peerDependencies: - postcss: ^8.4 + css-loader@5.2.7(webpack@5.104.1): dependencies: - postcss: 8.4.31 - dev: false + icss-utils: 5.1.0(postcss@8.5.6) + loader-utils: 2.0.4 + postcss: 8.5.6 + postcss-modules-extract-imports: 3.0.0(postcss@8.5.6) + postcss-modules-local-by-default: 4.0.3(postcss@8.5.6) + postcss-modules-scope: 3.0.0(postcss@8.5.6) + postcss-modules-values: 4.0.0(postcss@8.5.6) + postcss-value-parser: 4.2.0 + schema-utils: 3.3.0 + semver: 7.7.3 + webpack: 5.104.1 + + css-prefers-color-scheme@9.0.1(postcss@8.5.6): + dependencies: + postcss: 8.5.6 - /css-select@4.3.0: - resolution: - { - integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==, - } + css-select@4.3.0: dependencies: boolbase: 1.0.0 css-what: 6.1.0 domhandler: 4.3.1 domutils: 2.8.0 nth-check: 2.1.1 - dev: false - /css-select@5.1.0: - resolution: - { - integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==, - } + css-select@5.1.0: dependencies: boolbase: 1.0.0 css-what: 6.1.0 domhandler: 5.0.3 - domutils: 3.1.0 + domutils: 3.2.2 nth-check: 2.1.1 - dev: false - /css-tree@1.1.3: - resolution: - { - integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==, - } - engines: { node: '>=8.0.0' } + css-tree@1.1.3: dependencies: mdn-data: 2.0.14 source-map: 0.6.1 - dev: false - /css-tree@2.2.1: - resolution: - { - integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==, - } - engines: { node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0' } + css-tree@2.2.1: dependencies: mdn-data: 2.0.28 - source-map-js: 1.0.2 - dev: false + source-map-js: 1.2.1 - /css-tree@2.3.1: - resolution: - { - integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==, - } - engines: { node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0 } + css-tree@2.3.1: dependencies: mdn-data: 2.0.30 source-map-js: 1.0.2 - /css-what@6.1.0: - resolution: - { - integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==, - } - engines: { node: '>= 6' } - dev: false + css-what@6.1.0: {} - /css@2.2.4: - resolution: - { - integrity: sha512-oUnjmWpy0niI3x/mPL8dVEI1l7MnG3+HHyRPHf+YFSbK+svOhXpmSOcDURUh2aOCgl2grzrOPt1nHLuCVFULLw==, - } + css@2.2.4: dependencies: inherits: 2.0.4 source-map: 0.6.1 source-map-resolve: 0.5.3 urix: 0.1.0 - dev: true - - /cssdb@7.9.0: - resolution: - { - integrity: sha512-WPMT9seTQq6fPAa1yN4zjgZZeoTriSN2LqW9C+otjar12DQIWA4LuSfFrvFJiKp4oD0xIk1vumDLw8K9ur4NBw==, - } - dev: false - - /cssesc@3.0.0: - resolution: - { - integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==, - } - engines: { node: '>=4' } - hasBin: true - /cssnano-preset-default@5.2.14(postcss@8.4.31): - resolution: - { - integrity: sha512-t0SFesj/ZV2OTylqQVOrFgEh5uanxbO6ZAdeCrNsUQ6fVuXwYTxJPNAGvGTxHbD68ldIJNec7PyYZDBrfDQ+6A==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 - dependencies: - css-declaration-sorter: 6.4.1(postcss@8.4.31) - cssnano-utils: 3.1.0(postcss@8.4.31) - postcss: 8.4.31 - postcss-calc: 8.2.4(postcss@8.4.31) - postcss-colormin: 5.3.1(postcss@8.4.31) - postcss-convert-values: 5.1.3(postcss@8.4.31) - postcss-discard-comments: 5.1.2(postcss@8.4.31) - postcss-discard-duplicates: 5.1.0(postcss@8.4.31) - postcss-discard-empty: 5.1.1(postcss@8.4.31) - postcss-discard-overridden: 5.1.0(postcss@8.4.31) - postcss-merge-longhand: 5.1.7(postcss@8.4.31) - postcss-merge-rules: 5.1.4(postcss@8.4.31) - postcss-minify-font-values: 5.1.0(postcss@8.4.31) - postcss-minify-gradients: 5.1.1(postcss@8.4.31) - postcss-minify-params: 5.1.4(postcss@8.4.31) - postcss-minify-selectors: 5.2.1(postcss@8.4.31) - postcss-normalize-charset: 5.1.0(postcss@8.4.31) - postcss-normalize-display-values: 5.1.0(postcss@8.4.31) - postcss-normalize-positions: 5.1.1(postcss@8.4.31) - postcss-normalize-repeat-style: 5.1.1(postcss@8.4.31) - postcss-normalize-string: 5.1.0(postcss@8.4.31) - postcss-normalize-timing-functions: 5.1.0(postcss@8.4.31) - postcss-normalize-unicode: 5.1.1(postcss@8.4.31) - postcss-normalize-url: 5.1.0(postcss@8.4.31) - postcss-normalize-whitespace: 5.1.1(postcss@8.4.31) - postcss-ordered-values: 5.1.3(postcss@8.4.31) - postcss-reduce-initial: 5.1.2(postcss@8.4.31) - postcss-reduce-transforms: 5.1.0(postcss@8.4.31) - postcss-svgo: 5.1.0(postcss@8.4.31) - postcss-unique-selectors: 5.1.1(postcss@8.4.31) - dev: false - - /cssnano-preset-default@6.0.1(postcss@8.4.31): - resolution: - { - integrity: sha512-7VzyFZ5zEB1+l1nToKyrRkuaJIx0zi/1npjvZfbBwbtNTzhLtlvYraK/7/uqmX2Wb2aQtd983uuGw79jAjLSuQ==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + cssdb@8.0.2: {} + + cssesc@3.0.0: {} + + cssnano-preset-default@5.2.14(postcss@8.5.6): + dependencies: + css-declaration-sorter: 6.4.1(postcss@8.5.6) + cssnano-utils: 3.1.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-calc: 8.2.4(postcss@8.5.6) + postcss-colormin: 5.3.1(postcss@8.5.6) + postcss-convert-values: 5.1.3(postcss@8.5.6) + postcss-discard-comments: 5.1.2(postcss@8.5.6) + postcss-discard-duplicates: 5.1.0(postcss@8.5.6) + postcss-discard-empty: 5.1.1(postcss@8.5.6) + postcss-discard-overridden: 5.1.0(postcss@8.5.6) + postcss-merge-longhand: 5.1.7(postcss@8.5.6) + postcss-merge-rules: 5.1.4(postcss@8.5.6) + postcss-minify-font-values: 5.1.0(postcss@8.5.6) + postcss-minify-gradients: 5.1.1(postcss@8.5.6) + postcss-minify-params: 5.1.4(postcss@8.5.6) + postcss-minify-selectors: 5.2.1(postcss@8.5.6) + postcss-normalize-charset: 5.1.0(postcss@8.5.6) + postcss-normalize-display-values: 5.1.0(postcss@8.5.6) + postcss-normalize-positions: 5.1.1(postcss@8.5.6) + postcss-normalize-repeat-style: 5.1.1(postcss@8.5.6) + postcss-normalize-string: 5.1.0(postcss@8.5.6) + postcss-normalize-timing-functions: 5.1.0(postcss@8.5.6) + postcss-normalize-unicode: 5.1.1(postcss@8.5.6) + postcss-normalize-url: 5.1.0(postcss@8.5.6) + postcss-normalize-whitespace: 5.1.1(postcss@8.5.6) + postcss-ordered-values: 5.1.3(postcss@8.5.6) + postcss-reduce-initial: 5.1.2(postcss@8.5.6) + postcss-reduce-transforms: 5.1.0(postcss@8.5.6) + postcss-svgo: 5.1.0(postcss@8.5.6) + postcss-unique-selectors: 5.1.1(postcss@8.5.6) + + cssnano-preset-default@7.0.3(postcss@8.5.6): + dependencies: + browserslist: 4.28.1 + css-declaration-sorter: 7.2.0(postcss@8.5.6) + cssnano-utils: 5.0.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-calc: 10.0.0(postcss@8.5.6) + postcss-colormin: 7.0.1(postcss@8.5.6) + postcss-convert-values: 7.0.1(postcss@8.5.6) + postcss-discard-comments: 7.0.1(postcss@8.5.6) + postcss-discard-duplicates: 7.0.0(postcss@8.5.6) + postcss-discard-empty: 7.0.0(postcss@8.5.6) + postcss-discard-overridden: 7.0.0(postcss@8.5.6) + postcss-merge-longhand: 7.0.2(postcss@8.5.6) + postcss-merge-rules: 7.0.2(postcss@8.5.6) + postcss-minify-font-values: 7.0.0(postcss@8.5.6) + postcss-minify-gradients: 7.0.0(postcss@8.5.6) + postcss-minify-params: 7.0.1(postcss@8.5.6) + postcss-minify-selectors: 7.0.2(postcss@8.5.6) + postcss-normalize-charset: 7.0.0(postcss@8.5.6) + postcss-normalize-display-values: 7.0.0(postcss@8.5.6) + postcss-normalize-positions: 7.0.0(postcss@8.5.6) + postcss-normalize-repeat-style: 7.0.0(postcss@8.5.6) + postcss-normalize-string: 7.0.0(postcss@8.5.6) + postcss-normalize-timing-functions: 7.0.0(postcss@8.5.6) + postcss-normalize-unicode: 7.0.1(postcss@8.5.6) + postcss-normalize-url: 7.0.0(postcss@8.5.6) + postcss-normalize-whitespace: 7.0.0(postcss@8.5.6) + postcss-ordered-values: 7.0.1(postcss@8.5.6) + postcss-reduce-initial: 7.0.1(postcss@8.5.6) + postcss-reduce-transforms: 7.0.0(postcss@8.5.6) + postcss-svgo: 7.0.1(postcss@8.5.6) + postcss-unique-selectors: 7.0.1(postcss@8.5.6) + + cssnano-utils@3.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + cssnano-utils@5.0.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + cssnano@5.1.15(postcss@8.5.6): + dependencies: + cssnano-preset-default: 5.2.14(postcss@8.5.6) + lilconfig: 2.1.0 + postcss: 8.5.6 + yaml: 1.10.2 + + cssnano@7.0.3(postcss@8.5.6): dependencies: - css-declaration-sorter: 6.4.1(postcss@8.4.31) - cssnano-utils: 4.0.0(postcss@8.4.31) - postcss: 8.4.31 - postcss-calc: 9.0.1(postcss@8.4.31) - postcss-colormin: 6.0.0(postcss@8.4.31) - postcss-convert-values: 6.0.0(postcss@8.4.31) - postcss-discard-comments: 6.0.0(postcss@8.4.31) - postcss-discard-duplicates: 6.0.0(postcss@8.4.31) - postcss-discard-empty: 6.0.0(postcss@8.4.31) - postcss-discard-overridden: 6.0.0(postcss@8.4.31) - postcss-merge-longhand: 6.0.0(postcss@8.4.31) - postcss-merge-rules: 6.0.1(postcss@8.4.31) - postcss-minify-font-values: 6.0.0(postcss@8.4.31) - postcss-minify-gradients: 6.0.0(postcss@8.4.31) - postcss-minify-params: 6.0.0(postcss@8.4.31) - postcss-minify-selectors: 6.0.0(postcss@8.4.31) - postcss-normalize-charset: 6.0.0(postcss@8.4.31) - postcss-normalize-display-values: 6.0.0(postcss@8.4.31) - postcss-normalize-positions: 6.0.0(postcss@8.4.31) - postcss-normalize-repeat-style: 6.0.0(postcss@8.4.31) - postcss-normalize-string: 6.0.0(postcss@8.4.31) - postcss-normalize-timing-functions: 6.0.0(postcss@8.4.31) - postcss-normalize-unicode: 6.0.0(postcss@8.4.31) - postcss-normalize-url: 6.0.0(postcss@8.4.31) - postcss-normalize-whitespace: 6.0.0(postcss@8.4.31) - postcss-ordered-values: 6.0.0(postcss@8.4.31) - postcss-reduce-initial: 6.0.0(postcss@8.4.31) - postcss-reduce-transforms: 6.0.0(postcss@8.4.31) - postcss-svgo: 6.0.0(postcss@8.4.31) - postcss-unique-selectors: 6.0.0(postcss@8.4.31) - dev: false - - /cssnano-utils@3.1.0(postcss@8.4.31): - resolution: - { - integrity: sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + cssnano-preset-default: 7.0.3(postcss@8.5.6) + lilconfig: 3.1.3 + postcss: 8.5.6 + + csso@4.2.0: dependencies: - postcss: 8.4.31 - dev: false + css-tree: 1.1.3 - /cssnano-utils@4.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-Z39TLP+1E0KUcd7LGyF4qMfu8ZufI0rDzhdyAMsa/8UyNUU8wpS0fhdBxbQbv32r64ea00h4878gommRVg2BHw==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + csso@5.0.5: dependencies: - postcss: 8.4.31 - dev: false + css-tree: 2.2.1 - /cssnano@5.1.15(postcss@8.4.31): - resolution: - { - integrity: sha512-j+BKgDcLDQA+eDifLx0EO4XSA56b7uut3BQFH+wbSaSTuGLuiyTa/wbRYthUXX8LC9mLg+WWKe8h+qJuwTAbHw==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + cssstyle@4.6.0: dependencies: - cssnano-preset-default: 5.2.14(postcss@8.4.31) - lilconfig: 2.1.0 - postcss: 8.4.31 - yaml: 1.10.2 - dev: false + '@asamuzakjp/css-color': 3.2.0 + rrweb-cssom: 0.8.0 - /cssnano@6.0.1(postcss@8.4.31): - resolution: - { - integrity: sha512-fVO1JdJ0LSdIGJq68eIxOqFpIJrZqXUsBt8fkrBcztCQqAjQD51OhZp7tc0ImcbwXD4k7ny84QTV90nZhmqbkg==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + csstype@3.1.2: {} + + cuint@0.2.2: {} + + cyclist@1.0.2: {} + + dargs@8.1.0: {} + + data-urls@5.0.0: dependencies: - cssnano-preset-default: 6.0.1(postcss@8.4.31) - lilconfig: 2.1.0 - postcss: 8.4.31 - dev: false + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 - /csso@4.2.0: - resolution: - { - integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==, - } - engines: { node: '>=8.0.0' } + date-fns@2.30.0: dependencies: - css-tree: 1.1.3 - dev: false + '@babel/runtime': 7.24.5 - /csso@5.0.5: - resolution: - { - integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==, - } - engines: { node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0' } + de-indent@1.0.2: {} + + deasync@0.1.29: dependencies: - css-tree: 2.2.1 - dev: false - - /cssom@0.3.8: - resolution: - { - integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==, - } - dev: true - - /cssom@0.4.4: - resolution: - { - integrity: sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==, - } - dev: true - - /cssstyle@2.3.0: - resolution: - { - integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==, - } - engines: { node: '>=8' } - dependencies: - cssom: 0.3.8 - dev: true - - /csstype@3.1.2: - resolution: - { - integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==, - } - - /cuint@0.2.2: - resolution: - { - integrity: sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw==, - } - dev: false - - /cyclist@1.0.2: - resolution: - { - integrity: sha512-0sVXIohTfLqVIW3kb/0n6IiWF3Ifj5nm2XaSrLq2DI6fKIGa2fYAZdk917rUneaeLVpYfFcyXE2ft0fe3remsA==, - } - dev: false - - /dargs@7.0.0: - resolution: - { - integrity: sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg==, - } - engines: { node: '>=8' } - dev: true - - /data-urls@2.0.0: - resolution: - { - integrity: sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==, - } - engines: { node: '>=10' } - dependencies: - abab: 2.0.6 - whatwg-mimetype: 2.3.0 - whatwg-url: 8.7.0 - dev: true - - /date-fns@2.30.0: - resolution: - { - integrity: sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==, - } - engines: { node: '>=0.11' } - dependencies: - '@babel/runtime': 7.23.1 - dev: false - - /de-indent@1.0.2: - resolution: - { - integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==, - } - - /debug@2.6.9: - resolution: - { - integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==, - } - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + bindings: 1.5.0 + node-addon-api: 1.7.2 + + debounce@1.2.1: {} + + debug@2.6.9: dependencies: ms: 2.0.0 - /debug@3.2.7: - resolution: - { - integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==, - } - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + debug@3.2.7: dependencies: ms: 2.1.3 - dev: true - /debug@4.3.4: - resolution: - { - integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==, - } - engines: { node: '>=6.0' } - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true + debug@4.3.4: dependencies: ms: 2.1.2 - /decache@4.6.2: - resolution: - { - integrity: sha512-2LPqkLeu8XWHU8qNCS3kcF6sCcb5zIzvWaAHYSvPfwhdd7mHuah29NssMzrTYyHN4F5oFy2ko9OBYxegtU0FEw==, - } + debug@4.3.6: + dependencies: + ms: 2.1.2 + + debug@4.4.1: + dependencies: + ms: 2.1.3 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decache@4.6.2: dependencies: callsite: 1.0.0 - dev: true - /decamelize-keys@1.1.1: - resolution: - { - integrity: sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==, - } - engines: { node: '>=0.10.0' } + decamelize-keys@1.1.1: dependencies: decamelize: 1.2.0 map-obj: 1.0.1 - dev: true - - /decamelize@1.2.0: - resolution: - { - integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==, - } - engines: { node: '>=0.10.0' } - dev: true - - /decamelize@5.0.1: - resolution: - { - integrity: sha512-VfxadyCECXgQlkoEAjeghAr5gY3Hf+IKjKb+X8tGVDtveCjN+USwprd2q3QXBR9T1+x2DG0XZF5/w+7HAtSaXA==, - } - engines: { node: '>=10' } - dev: true - - /decimal.js@10.4.3: - resolution: - { - integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==, - } - dev: true - - /decode-uri-component@0.2.2: - resolution: - { - integrity: sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==, - } - engines: { node: '>=0.10' } - - /dedent@0.7.0: - resolution: - { - integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==, - } - dev: true - - /deep-is@0.1.4: - resolution: - { - integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==, - } - dev: true - - /deepmerge@4.3.1: - resolution: - { - integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==, - } - engines: { node: '>=0.10.0' } - - /define-data-property@1.1.0: - resolution: - { - integrity: sha512-UzGwzcjyv3OtAvolTj1GoyNYzfFR+iqbGjcnBEENZVCpM4/Ng1yhGNvS3lR/xDS74Tb2wGG9WzNSNIOS9UVb2g==, - } - engines: { node: '>= 0.4' } + + decamelize@1.2.0: {} + + decamelize@5.0.1: {} + + decimal.js@10.6.0: {} + + decode-uri-component@0.2.2: {} + + dedent@1.7.0: {} + + deep-is@0.1.4: {} + + deepmerge@4.3.1: {} + + define-data-property@1.1.0: dependencies: - get-intrinsic: 1.2.1 - gopd: 1.0.1 + get-intrinsic: 1.3.0 + gopd: 1.2.0 has-property-descriptors: 1.0.0 - /define-properties@1.2.1: - resolution: - { - integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==, - } - engines: { node: '>= 0.4' } + define-properties@1.2.1: dependencies: define-data-property: 1.1.0 has-property-descriptors: 1.0.0 object-keys: 1.1.1 - /define-property@0.2.5: - resolution: - { - integrity: sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==, - } - engines: { node: '>=0.10.0' } + define-property@0.2.5: dependencies: is-descriptor: 0.1.6 - dev: false - /define-property@1.0.0: - resolution: - { - integrity: sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==, - } - engines: { node: '>=0.10.0' } + define-property@1.0.0: dependencies: is-descriptor: 1.0.2 - dev: false - /define-property@2.0.2: - resolution: - { - integrity: sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==, - } - engines: { node: '>=0.10.0' } + define-property@2.0.2: dependencies: is-descriptor: 1.0.2 isobject: 3.0.1 - dev: false - - /defu@5.0.1: - resolution: - { - integrity: sha512-EPS1carKg+dkEVy3qNTqIdp2qV7mUP08nIsupfwQpz++slCVRw7qbQyWvSTig+kFPwz2XXp5/kIIkH+CwrJKkQ==, - } - dev: false - - /defu@6.1.2: - resolution: - { - integrity: sha512-+uO4+qr7msjNNWKYPHqN/3+Dx3NFkmIzayk2L1MyZQlvgZb/J1A0fo410dpKrN2SnqFjt8n4JL8fDJE0wIgjFQ==, - } - - /delayed-stream@1.0.0: - resolution: - { - integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==, - } - engines: { node: '>=0.4.0' } - dev: true - - /depd@2.0.0: - resolution: - { - integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==, - } - engines: { node: '>= 0.8' } - dev: false - - /des.js@1.1.0: - resolution: - { - integrity: sha512-r17GxjhUCjSRy8aiJpr8/UadFIzMzJGexI3Nmz4ADi9LYSFx4gTBp80+NaX/YsXWWLhpZ7v/v/ubEc/bCNfKwg==, - } + + defu@5.0.1: {} + + defu@6.1.2: {} + + defu@6.1.4: {} + + delayed-stream@1.0.0: {} + + depd@2.0.0: {} + + des.js@1.1.0: dependencies: inherits: 2.0.4 minimalistic-assert: 1.0.1 - dev: false - - /destr@1.2.2: - resolution: - { - integrity: sha512-lrbCJwD9saUQrqUfXvl6qoM+QN3W7tLV5pAOs+OqOmopCCz/JkE05MHedJR1xfk4IAnZuJXPVuN5+7jNA2ZCiA==, - } - dev: false - - /destr@2.0.1: - resolution: - { - integrity: sha512-M1Ob1zPSIvlARiJUkKqvAZ3VAqQY6Jcuth/pBKQ2b1dX/Qx0OnJ8Vux6J2H5PTMQeRzWrrbTu70VxBfv/OPDJA==, - } - - /destroy@1.2.0: - resolution: - { - integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==, - } - engines: { node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16 } - dev: false - - /detect-indent@5.0.0: - resolution: - { - integrity: sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g==, - } - engines: { node: '>=4' } - dev: false - - /detect-newline@3.1.0: - resolution: - { - integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==, - } - engines: { node: '>=8' } - dev: true - - /devalue@2.0.1: - resolution: - { - integrity: sha512-I2TiqT5iWBEyB8GRfTDP0hiLZ0YeDJZ+upDxjBfOC2lebO5LezQMv7QvIUTzdb64jQyAKLf1AHADtGN+jw6v8Q==, - } - dev: false - - /dialog-polyfill@0.4.10: - resolution: - { - integrity: sha512-j5yGMkP8T00UFgyO+78OxiN5vC5dzRQF3BEio+LhNvDbyfxWBsi3sfPArDm54VloaJwy2hm3erEiDWqHRC8rzw==, - } - dev: false - - /diff-sequences@27.5.1: - resolution: - { - integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - dev: true - - /diff@4.0.2: - resolution: - { - integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==, - } - engines: { node: '>=0.3.1' } - dev: true - - /diffie-hellman@5.0.3: - resolution: - { - integrity: sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==, - } + + destr@2.0.3: {} + + destroy@1.2.0: {} + + detect-indent@5.0.0: {} + + detect-newline@3.1.0: {} + + devalue@2.0.1: {} + + dialog-polyfill@0.4.10: {} + + diffie-hellman@5.0.3: dependencies: bn.js: 4.12.0 miller-rabin: 4.0.1 randombytes: 2.1.0 - dev: false - /dir-glob@3.0.1: - resolution: - { - integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==, - } - engines: { node: '>=8' } + dijkstrajs@1.0.3: {} + + dir-glob@3.0.1: dependencies: path-type: 4.0.0 - /doctrine@2.1.0: - resolution: - { - integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==, - } - engines: { node: '>=0.10.0' } + doctrine@2.1.0: dependencies: esutils: 2.0.3 - dev: true - /doctrine@3.0.0: - resolution: - { - integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==, - } - engines: { node: '>=6.0.0' } + doctrine@3.0.0: dependencies: esutils: 2.0.3 - dev: true - /dom-converter@0.2.0: - resolution: - { - integrity: sha512-gd3ypIPfOMr9h5jIKq8E3sHOTCjeirnl0WK5ZdS1AW0Odt0b1PaWaHdJ4Qk4klv+YB9aJBS7mESXjFoDQPu6DA==, - } + dom-converter@0.2.0: dependencies: utila: 0.4.0 - dev: false - /dom-event-types@1.1.0: - resolution: - { - integrity: sha512-jNCX+uNJ3v38BKvPbpki6j5ItVlnSqVV6vDWGS6rExzCMjsc39frLjm1n91o6YaKK6AZl0wLloItW6C6mr61BQ==, - } - dev: true + dom-event-types@1.1.0: {} - /dom-serializer@1.4.1: - resolution: - { - integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==, - } + dom-serializer@1.4.1: dependencies: domelementtype: 2.3.0 domhandler: 4.3.1 entities: 2.2.0 - dev: false - /dom-serializer@2.0.0: - resolution: - { - integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==, - } + dom-serializer@2.0.0: dependencies: domelementtype: 2.3.0 domhandler: 5.0.3 entities: 4.5.0 - /domain-browser@1.2.0: - resolution: - { - integrity: sha512-jnjyiM6eRyZl2H+W8Q/zLMA481hzi0eszAaBUzIVnmYVDBbnLxVNnfu1HgEBvCbL+71FrxMl3E6lpKH7Ge3OXA==, - } - engines: { node: '>=0.4', npm: '>=1.2' } - dev: false - - /domelementtype@2.3.0: - resolution: - { - integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==, - } - - /domexception@2.0.1: - resolution: - { - integrity: sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==, - } - engines: { node: '>=8' } - dependencies: - webidl-conversions: 5.0.0 - dev: true - - /domhandler@4.3.1: - resolution: - { - integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==, - } - engines: { node: '>= 4' } + domain-browser@1.2.0: {} + + domelementtype@2.3.0: {} + + domhandler@4.3.1: dependencies: domelementtype: 2.3.0 - dev: false - /domhandler@5.0.3: - resolution: - { - integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==, - } - engines: { node: '>= 4' } + domhandler@5.0.3: dependencies: domelementtype: 2.3.0 - /domutils@2.8.0: - resolution: - { - integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==, - } + domutils@2.8.0: dependencies: dom-serializer: 1.4.1 domelementtype: 2.3.0 domhandler: 4.3.1 - dev: false - /domutils@3.1.0: - resolution: - { - integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==, - } + domutils@3.2.2: dependencies: dom-serializer: 2.0.0 domelementtype: 2.3.0 domhandler: 5.0.3 - /dot-case@3.0.4: - resolution: - { - integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==, - } - dependencies: - no-case: 3.0.4 - tslib: 2.6.2 - dev: false + dot-case@3.0.4: + dependencies: + no-case: 3.0.4 + tslib: 2.6.2 + + dot-prop@5.3.0: + dependencies: + is-obj: 2.0.0 + + dotenv@16.6.1: {} + + dotenv@17.2.3: {} + + dotenv@8.6.0: {} + + dotenv@9.0.2: {} - /dot-prop@5.3.0: - resolution: - { - integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==, - } - engines: { node: '>=8' } + dunder-proto@1.0.1: dependencies: - is-obj: 2.0.0 + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + duplexer@0.1.2: {} - /dotenv@16.3.1: - resolution: - { - integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==, - } - engines: { node: '>=12' } - - /dotenv@8.6.0: - resolution: - { - integrity: sha512-IrPdXQsk2BbzvCBGBOTmmSH5SodmqZNt4ERAZDmW4CT+tL8VtvinqywuANaFu4bOMWki16nqf0e4oC0QIaDr/g==, - } - engines: { node: '>=10' } - dev: false - - /dotenv@9.0.2: - resolution: - { - integrity: sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==, - } - engines: { node: '>=10' } - dev: false - - /duplexer@0.1.2: - resolution: - { - integrity: sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==, - } - dev: false - - /duplexify@3.7.1: - resolution: - { - integrity: sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==, - } + duplexify@3.7.1: dependencies: end-of-stream: 1.4.4 inherits: 2.0.4 readable-stream: 2.3.8 stream-shift: 1.0.1 - dev: false - /duplexify@4.1.2: - resolution: - { - integrity: sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==, - } - requiresBuild: true + duplexify@4.1.2: dependencies: end-of-stream: 1.4.4 inherits: 2.0.4 readable-stream: 3.6.2 stream-shift: 1.0.1 - dev: false optional: true - /eastasianwidth@0.2.0: - resolution: - { - integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==, - } - dev: true + eastasianwidth@0.2.0: {} - /ecdsa-sig-formatter@1.0.11: - resolution: - { - integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==, - } - requiresBuild: true + ecdsa-sig-formatter@1.0.11: dependencies: safe-buffer: 5.2.1 - dev: false optional: true - /editorconfig@1.0.4: - resolution: - { - integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==, - } - engines: { node: '>=14' } - hasBin: true + editorconfig@1.0.4: dependencies: '@one-ini/wasm': 0.1.1 commander: 10.0.1 minimatch: 9.0.1 - semver: 7.5.4 - dev: true - - /ee-first@1.1.1: - resolution: - { - integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==, - } - dev: false - - /electron-to-chromium@1.4.537: - resolution: - { - integrity: sha512-W1+g9qs9hviII0HAwOdehGYkr+zt7KKdmCcJcjH0mYg6oL8+ioT3Skjmt7BLoAQqXhjf40AXd+HlR4oAWMlXjA==, - } - - /elliptic@6.5.4: - resolution: - { - integrity: sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==, - } + semver: 7.7.3 + + ee-first@1.1.1: {} + + ejs@3.1.10: + dependencies: + jake: 10.9.4 + optional: true + + electron-to-chromium@1.5.267: {} + + elliptic@6.6.0: dependencies: bn.js: 4.12.0 brorand: 1.1.0 @@ -10131,138 +14605,71 @@ packages: inherits: 2.0.4 minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 - dev: false - - /emittery@0.8.1: - resolution: - { - integrity: sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==, - } - engines: { node: '>=10' } - dev: true - - /emoji-regex@8.0.0: - resolution: - { - integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==, - } - - /emoji-regex@9.2.2: - resolution: - { - integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==, - } - dev: true - - /emojis-list@3.0.0: - resolution: - { - integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==, - } - engines: { node: '>= 4' } - - /encodeurl@1.0.2: - resolution: - { - integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==, - } - engines: { node: '>= 0.8' } - dev: false - - /end-of-stream@1.4.4: - resolution: - { - integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==, - } + + emittery@0.13.1: {} + + emoji-regex@10.4.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + emojis-list@3.0.0: {} + + encodeurl@1.0.2: {} + + encodeurl@2.0.0: {} + + end-of-stream@1.4.4: dependencies: once: 1.4.0 - dev: false - /enhanced-resolve@4.5.0: - resolution: - { - integrity: sha512-Nv9m36S/vxpsI+Hc4/ZGRs0n9mXqSWGGq49zxb/cJfPAQMbUtttJAlNPS4AQzaBdw/pKskw5bMbekT/Y7W/Wlg==, - } - engines: { node: '>=6.9.0' } + enhanced-resolve@4.5.0: dependencies: graceful-fs: 4.2.11 memory-fs: 0.5.0 tapable: 1.1.3 - /enhanced-resolve@5.15.0: - resolution: - { - integrity: sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==, - } - engines: { node: '>=10.13.0' } + enhanced-resolve@5.18.4: dependencies: graceful-fs: 4.2.11 - tapable: 2.2.1 - - /ent@2.2.0: - resolution: - { - integrity: sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA==, - } - requiresBuild: true - dev: false + tapable: 2.3.0 + + ent@2.2.0: optional: true - /entities@2.2.0: - resolution: - { - integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==, - } - dev: false - - /entities@4.5.0: - resolution: - { - integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==, - } - engines: { node: '>=0.12' } - - /errno@0.1.8: - resolution: - { - integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==, - } - hasBin: true + entities@2.2.0: {} + + entities@4.5.0: {} + + entities@6.0.1: {} + + env-paths@2.2.1: {} + + environment@1.1.0: {} + + errno@0.1.8: dependencies: prr: 1.0.1 - /error-ex@1.3.2: - resolution: - { - integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==, - } + error-ex@1.3.2: dependencies: is-arrayish: 0.2.1 - /error-stack-parser@2.1.4: - resolution: - { - integrity: sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==, - } + error-stack-parser@2.1.4: dependencies: stackframe: 1.3.4 - dev: false - /es-abstract@1.22.2: - resolution: - { - integrity: sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA==, - } - engines: { node: '>= 0.4' } + es-abstract@1.22.2: dependencies: array-buffer-byte-length: 1.0.0 arraybuffer.prototype.slice: 1.0.2 available-typed-arrays: 1.0.5 call-bind: 1.0.2 - es-set-tostringtag: 2.0.1 + es-set-tostringtag: 2.1.0 es-to-primitive: 1.2.1 function.prototype.name: 1.1.6 - get-intrinsic: 1.2.1 + get-intrinsic: 1.3.0 get-symbol-description: 1.0.0 globalthis: 1.0.3 gopd: 1.0.1 @@ -10295,58 +14702,36 @@ packages: unbox-primitive: 1.0.2 which-typed-array: 1.1.11 - /es-array-method-boxes-properly@1.0.0: - resolution: - { - integrity: sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==, - } - dev: false - - /es-module-lexer@1.3.1: - resolution: - { - integrity: sha512-JUFAyicQV9mXc3YRxPnDlrfBKpqt6hUYzz9/boprUJHs4e4KVr3XwOF70doO6gwXUor6EWZJAyWAfKki84t20Q==, - } - - /es-set-tostringtag@2.0.1: - resolution: - { - integrity: sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==, - } - engines: { node: '>= 0.4' } + es-array-method-boxes-properly@1.0.0: {} + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@2.0.0: {} + + es-object-atoms@1.1.1: dependencies: - get-intrinsic: 1.2.1 - has: 1.0.3 - has-tostringtag: 1.0.0 + es-errors: 1.3.0 - /es-shim-unscopables@1.0.0: - resolution: - { - integrity: sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==, - } + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-shim-unscopables@1.0.0: dependencies: has: 1.0.3 - dev: true - /es-to-primitive@1.2.1: - resolution: - { - integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==, - } - engines: { node: '>= 0.4' } + es-to-primitive@1.2.1: dependencies: is-callable: 1.2.7 is-date-object: 1.0.5 is-symbol: 1.0.4 - /esbuild@0.18.20: - resolution: - { - integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==, - } - engines: { node: '>=12' } - hasBin: true - requiresBuild: true + esbuild@0.18.20: optionalDependencies: '@esbuild/android-arm': 0.18.20 '@esbuild/android-arm64': 0.18.20 @@ -10370,125 +14755,45 @@ packages: '@esbuild/win32-arm64': 0.18.20 '@esbuild/win32-ia32': 0.18.20 '@esbuild/win32-x64': 0.18.20 - dev: true - - /escalade@3.1.1: - resolution: - { - integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==, - } - engines: { node: '>=6' } - - /escape-html@1.0.3: - resolution: - { - integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==, - } - dev: false - - /escape-string-regexp@1.0.5: - resolution: - { - integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==, - } - engines: { node: '>=0.8.0' } - - /escape-string-regexp@2.0.0: - resolution: - { - integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==, - } - engines: { node: '>=8' } - dev: true - - /escape-string-regexp@4.0.0: - resolution: - { - integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==, - } - engines: { node: '>=10' } - - /escape-string-regexp@5.0.0: - resolution: - { - integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==, - } - engines: { node: '>=12' } - dev: true - - /escodegen@2.1.0: - resolution: - { - integrity: sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==, - } - engines: { node: '>=6.0' } - hasBin: true - dependencies: - esprima: 4.0.1 - estraverse: 5.3.0 - esutils: 2.0.3 - optionalDependencies: - source-map: 0.6.1 - dev: true - /eslint-config-prettier@9.0.0(eslint@8.53.0): - resolution: - { - integrity: sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==, - } - hasBin: true - peerDependencies: - eslint: '>=7.0.0' + escalade@3.2.0: {} + + escape-html@1.0.3: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@2.0.0: {} + + escape-string-regexp@4.0.0: {} + + escape-string-regexp@5.0.0: {} + + eslint-config-prettier@10.1.8(eslint@8.57.1): dependencies: - eslint: 8.53.0 - dev: true + eslint: 8.57.1 - /eslint-config-standard@17.1.0(eslint-plugin-import@2.28.1)(eslint-plugin-n@15.7.0)(eslint-plugin-promise@6.1.1)(eslint@8.53.0): - resolution: - { - integrity: sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==, - } - engines: { node: '>=12.0.0' } - peerDependencies: - eslint: ^8.0.1 - eslint-plugin-import: ^2.25.2 - eslint-plugin-n: '^15.0.0 || ^16.0.0 ' - eslint-plugin-promise: ^6.0.0 + eslint-config-standard@17.1.0(eslint-plugin-import@2.28.1)(eslint-plugin-n@15.7.0(eslint@8.57.1))(eslint-plugin-promise@6.1.1(eslint@8.57.1))(eslint@8.57.1): dependencies: - eslint: 8.53.0 - eslint-plugin-import: 2.28.1(@typescript-eslint/parser@6.7.3)(eslint-import-resolver-typescript@3.6.1)(eslint@8.53.0) - eslint-plugin-n: 15.7.0(eslint@8.53.0) - eslint-plugin-promise: 6.1.1(eslint@8.53.0) - dev: true + eslint: 8.57.1 + eslint-plugin-import: 2.28.1(@typescript-eslint/parser@6.7.3(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) + eslint-plugin-n: 15.7.0(eslint@8.57.1) + eslint-plugin-promise: 6.1.1(eslint@8.57.1) - /eslint-import-resolver-node@0.3.9: - resolution: - { - integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==, - } + eslint-import-resolver-node@0.3.9: dependencies: debug: 3.2.7 is-core-module: 2.13.0 resolve: 1.22.6 transitivePeerDependencies: - supports-color - dev: true - /eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.7.3)(eslint-plugin-import@2.28.1)(eslint@8.53.0): - resolution: - { - integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==, - } - engines: { node: ^14.18.0 || >=16.0.0 } - peerDependencies: - eslint: '*' - eslint-plugin-import: '*' + eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.7.3(eslint@8.57.1)(typescript@4.9.5))(eslint-plugin-import@2.28.1)(eslint@8.57.1): dependencies: - debug: 4.3.4 - enhanced-resolve: 5.15.0 - eslint: 8.53.0 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.3)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.53.0) - eslint-plugin-import: 2.28.1(@typescript-eslint/parser@6.7.3)(eslint-import-resolver-typescript@3.6.1)(eslint@8.53.0) + debug: 4.4.1 + enhanced-resolve: 5.18.4 + eslint: 8.57.1 + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.3(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) + eslint-plugin-import: 2.28.1(@typescript-eslint/parser@6.7.3(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) fast-glob: 3.3.1 get-tsconfig: 4.7.2 is-core-module: 2.13.0 @@ -10498,92 +14803,41 @@ packages: - eslint-import-resolver-node - eslint-import-resolver-webpack - supports-color - dev: true - /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.3)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.53.0): - resolution: - { - integrity: sha512-aWajIYfsqCKRDgUfjEXNN/JlrzauMuSEy5sbd7WXbtW3EH6A6MpwEh42c7qD+MqQo9QMJ6fWLAeIJynx0g6OAw==, - } - engines: { node: '>=4' } - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: '*' - eslint-import-resolver-node: '*' - eslint-import-resolver-typescript: '*' - eslint-import-resolver-webpack: '*' - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true - eslint: - optional: true - eslint-import-resolver-node: - optional: true - eslint-import-resolver-typescript: - optional: true - eslint-import-resolver-webpack: - optional: true + eslint-module-utils@2.8.0(@typescript-eslint/parser@6.7.3(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1): dependencies: - '@typescript-eslint/parser': 6.7.3(eslint@8.53.0)(typescript@4.9.5) debug: 3.2.7 - eslint: 8.53.0 + optionalDependencies: + '@typescript-eslint/parser': 6.7.3(eslint@8.57.1)(typescript@4.9.5) + eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.7.3)(eslint-plugin-import@2.28.1)(eslint@8.53.0) + eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.7.3(eslint@8.57.1)(typescript@4.9.5))(eslint-plugin-import@2.28.1)(eslint@8.57.1) transitivePeerDependencies: - supports-color - dev: true - /eslint-plugin-es@3.0.1(eslint@8.53.0): - resolution: - { - integrity: sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==, - } - engines: { node: '>=8.10.0' } - peerDependencies: - eslint: '>=4.19.1' + eslint-plugin-es@3.0.1(eslint@8.57.1): dependencies: - eslint: 8.53.0 + eslint: 8.57.1 eslint-utils: 2.1.0 regexpp: 3.2.0 - dev: true - /eslint-plugin-es@4.1.0(eslint@8.53.0): - resolution: - { - integrity: sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==, - } - engines: { node: '>=8.10.0' } - peerDependencies: - eslint: '>=4.19.1' + eslint-plugin-es@4.1.0(eslint@8.57.1): dependencies: - eslint: 8.53.0 + eslint: 8.57.1 eslint-utils: 2.1.0 regexpp: 3.2.0 - dev: true - /eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.3)(eslint-import-resolver-typescript@3.6.1)(eslint@8.53.0): - resolution: - { - integrity: sha512-9I9hFlITvOV55alzoKBI+K9q74kv0iKMeY6av5+umsNwayt59fz692daGyjR+oStBQgx6nwR9rXldDev3Clw+A==, - } - engines: { node: '>=4' } - peerDependencies: - '@typescript-eslint/parser': '*' - eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - peerDependenciesMeta: - '@typescript-eslint/parser': - optional: true + eslint-plugin-import@2.28.1(@typescript-eslint/parser@6.7.3(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1): dependencies: - '@typescript-eslint/parser': 6.7.3(eslint@8.53.0)(typescript@4.9.5) array-includes: 3.1.7 array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.2 array.prototype.flatmap: 1.3.2 debug: 3.2.7 doctrine: 2.1.0 - eslint: 8.53.0 + eslint: 8.57.1 eslint-import-resolver-node: 0.3.9 - eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.3)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.53.0) + eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.7.3(eslint@8.57.1)(typescript@4.9.5))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.57.1) has: 1.0.3 is-core-module: 2.13.0 is-glob: 4.0.3 @@ -10593,91 +14847,56 @@ packages: object.values: 1.1.7 semver: 6.3.1 tsconfig-paths: 3.14.2 + optionalDependencies: + '@typescript-eslint/parser': 6.7.3(eslint@8.57.1)(typescript@4.9.5) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack - supports-color - dev: true - /eslint-plugin-n@15.7.0(eslint@8.53.0): - resolution: - { - integrity: sha512-jDex9s7D/Qial8AGVIHq4W7NswpUD5DPDL2RH8Lzd9EloWUuvUkHfv4FRLMipH5q2UtyurorBkPeNi1wVWNh3Q==, - } - engines: { node: '>=12.22.0' } - peerDependencies: - eslint: '>=7.0.0' + eslint-plugin-n@15.7.0(eslint@8.57.1): dependencies: builtins: 5.0.1 - eslint: 8.53.0 - eslint-plugin-es: 4.1.0(eslint@8.53.0) - eslint-utils: 3.0.0(eslint@8.53.0) - ignore: 5.2.4 + eslint: 8.57.1 + eslint-plugin-es: 4.1.0(eslint@8.57.1) + eslint-utils: 3.0.0(eslint@8.57.1) + ignore: 5.3.1 is-core-module: 2.13.0 minimatch: 3.1.2 resolve: 1.22.6 - semver: 7.5.4 - dev: true + semver: 7.7.3 - /eslint-plugin-node@11.1.0(eslint@8.53.0): - resolution: - { - integrity: sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==, - } - engines: { node: '>=8.10.0' } - peerDependencies: - eslint: '>=5.16.0' + eslint-plugin-node@11.1.0(eslint@8.57.1): dependencies: - eslint: 8.53.0 - eslint-plugin-es: 3.0.1(eslint@8.53.0) + eslint: 8.57.1 + eslint-plugin-es: 3.0.1(eslint@8.57.1) eslint-utils: 2.1.0 - ignore: 5.2.4 + ignore: 5.3.1 minimatch: 3.1.2 resolve: 1.22.6 semver: 6.3.1 - dev: true - /eslint-plugin-nuxt@4.0.0(eslint@8.53.0): - resolution: - { - integrity: sha512-v3Vwdk8YKe52bAz8eSIDqQuTtfL/T1r9dSl1uhC5SyR5pgLxgKkQdxXVf/Bf6Ax7uyd9rHqiAuYVdqqDb7ILdA==, - } + eslint-plugin-nuxt@4.0.0(eslint@8.57.1): dependencies: - eslint-plugin-vue: 9.16.1(eslint@8.53.0) + eslint-plugin-vue: 9.33.0(eslint@8.57.1) semver: 7.5.4 - vue-eslint-parser: 9.3.1(eslint@8.53.0) + vue-eslint-parser: 9.3.1(eslint@8.57.1) transitivePeerDependencies: - eslint - supports-color - dev: true - /eslint-plugin-promise@6.1.1(eslint@8.53.0): - resolution: - { - integrity: sha512-tjqWDwVZQo7UIPMeDReOpUgHCmCiH+ePnVT+5zVapL0uuHnegBUs2smM13CzOs2Xb5+MHMRFTs9v24yjba4Oig==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } - peerDependencies: - eslint: ^7.0.0 || ^8.0.0 + eslint-plugin-promise@6.1.1(eslint@8.57.1): dependencies: - eslint: 8.53.0 - dev: true + eslint: 8.57.1 - /eslint-plugin-unicorn@44.0.2(eslint@8.53.0): - resolution: - { - integrity: sha512-GLIDX1wmeEqpGaKcnMcqRvMVsoabeF0Ton0EX4Th5u6Kmf7RM9WBl705AXFEsns56ESkEs0uyelLuUTvz9Tr0w==, - } - engines: { node: '>=14.18' } - peerDependencies: - eslint: '>=8.23.1' + eslint-plugin-unicorn@44.0.2(eslint@8.57.1): dependencies: - '@babel/helper-validator-identifier': 7.22.20 - ci-info: 3.8.0 + '@babel/helper-validator-identifier': 7.24.5 + ci-info: 3.9.0 clean-regexp: 1.0.0 - eslint: 8.53.0 - eslint-utils: 3.0.0(eslint@8.53.0) - esquery: 1.5.0 + eslint: 8.57.1 + eslint-utils: 3.0.0(eslint@8.57.1) + esquery: 1.6.0 indent-string: 4.0.0 is-builtin-module: 3.2.1 lodash: 4.17.21 @@ -10685,170 +14904,77 @@ packages: read-pkg-up: 7.0.1 regexp-tree: 0.1.27 safe-regex: 2.1.1 - semver: 7.5.4 + semver: 7.7.3 strip-indent: 3.0.0 - dev: true - - /eslint-plugin-vue@9.16.1(eslint@8.53.0): - resolution: - { - integrity: sha512-2FtnTqazA6aYONfDuOZTk0QzwhAwi7Z4+uJ7+GHeGxcKapjqWlDsRWDenvyG/utyOfAS5bVRmAG3cEWiYEz2bA==, - } - engines: { node: ^14.17.0 || >=16.0.0 } - peerDependencies: - eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 - dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) - eslint: 8.53.0 - natural-compare: 1.4.0 - nth-check: 2.1.1 - postcss-selector-parser: 6.0.13 - semver: 7.5.4 - vue-eslint-parser: 9.3.1(eslint@8.53.0) - xml-name-validator: 4.0.0 - transitivePeerDependencies: - - supports-color - dev: true - /eslint-plugin-vue@9.17.0(eslint@8.53.0): - resolution: - { - integrity: sha512-r7Bp79pxQk9I5XDP0k2dpUC7Ots3OSWgvGZNu3BxmKK6Zg7NgVtcOB6OCna5Kb9oQwJPl5hq183WD0SY5tZtIQ==, - } - engines: { node: ^14.17.0 || >=16.0.0 } - peerDependencies: - eslint: ^6.2.0 || ^7.0.0 || ^8.0.0 + eslint-plugin-vue@9.33.0(eslint@8.57.1): dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) - eslint: 8.53.0 + '@eslint-community/eslint-utils': 4.7.0(eslint@8.57.1) + eslint: 8.57.1 + globals: 13.24.0 natural-compare: 1.4.0 nth-check: 2.1.1 - postcss-selector-parser: 6.0.13 - semver: 7.5.4 - vue-eslint-parser: 9.3.1(eslint@8.53.0) + postcss-selector-parser: 6.1.2 + semver: 7.7.2 + vue-eslint-parser: 9.4.3(eslint@8.57.1) xml-name-validator: 4.0.0 transitivePeerDependencies: - supports-color - dev: true - /eslint-scope@4.0.3: - resolution: - { - integrity: sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==, - } - engines: { node: '>=4.0.0' } + eslint-scope@4.0.3: dependencies: esrecurse: 4.3.0 estraverse: 4.3.0 - dev: false - /eslint-scope@5.1.1: - resolution: - { - integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==, - } - engines: { node: '>=8.0.0' } + eslint-scope@5.1.1: dependencies: esrecurse: 4.3.0 estraverse: 4.3.0 - /eslint-scope@7.2.2: - resolution: - { - integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + eslint-scope@7.2.2: dependencies: esrecurse: 4.3.0 estraverse: 5.3.0 - dev: true - /eslint-utils@2.1.0: - resolution: - { - integrity: sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==, - } - engines: { node: '>=6' } + eslint-utils@2.1.0: dependencies: eslint-visitor-keys: 1.3.0 - dev: true - /eslint-utils@3.0.0(eslint@8.53.0): - resolution: - { - integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==, - } - engines: { node: ^10.0.0 || ^12.0.0 || >= 14.0.0 } - peerDependencies: - eslint: '>=5' + eslint-utils@3.0.0(eslint@8.57.1): dependencies: - eslint: 8.53.0 + eslint: 8.57.1 eslint-visitor-keys: 2.1.0 - dev: true - - /eslint-visitor-keys@1.3.0: - resolution: - { - integrity: sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==, - } - engines: { node: '>=4' } - dev: true - - /eslint-visitor-keys@2.1.0: - resolution: - { - integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==, - } - engines: { node: '>=10' } - dev: true - - /eslint-visitor-keys@3.4.3: - resolution: - { - integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } - dev: true - - /eslint-webpack-plugin@4.0.1(eslint@8.53.0)(webpack@5.87.0): - resolution: - { - integrity: sha512-fUFcXpui/FftGx3NzvWgLZXlLbu+m74sUxGEgxgoxYcUtkIQbS6SdNNZkS99m5ycb23TfoNYrDpp1k/CK5j6Hw==, - } - engines: { node: '>= 14.15.0' } - peerDependencies: - eslint: ^8.0.0 - webpack: ^5.0.0 + + eslint-visitor-keys@1.3.0: {} + + eslint-visitor-keys@2.1.0: {} + + eslint-visitor-keys@3.4.3: {} + + eslint-webpack-plugin@4.0.1(eslint@8.57.1)(webpack@5.104.1): dependencies: '@types/eslint': 8.44.3 - eslint: 8.53.0 + eslint: 8.57.1 jest-worker: 29.7.0 - micromatch: 4.0.5 + micromatch: 4.0.8 normalize-path: 3.0.0 - schema-utils: 4.2.0 - webpack: 5.87.0 - dev: true - - /eslint@8.53.0: - resolution: - { - integrity: sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } - hasBin: true + schema-utils: 4.3.3 + webpack: 5.104.1 + + eslint@8.57.1: dependencies: - '@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0) + '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1) '@eslint-community/regexpp': 4.9.0 - '@eslint/eslintrc': 2.1.3 - '@eslint/js': 8.53.0 - '@humanwhocodes/config-array': 0.11.13 + '@eslint/eslintrc': 2.1.4 + '@eslint/js': 8.57.1 + '@humanwhocodes/config-array': 0.13.0 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 '@ungap/structured-clone': 1.2.0 ajv: 6.12.6 chalk: 4.1.2 - cross-spawn: 7.0.3 - debug: 4.3.4 + cross-spawn: 7.0.6 + debug: 4.3.6 doctrine: 3.0.0 escape-string-regexp: 4.0.0 eslint-scope: 7.2.2 @@ -10860,9 +14986,9 @@ packages: file-entry-cache: 6.0.1 find-up: 5.0.0 glob-parent: 6.0.2 - globals: 13.22.0 + globals: 13.24.0 graphemer: 1.4.0 - ignore: 5.2.4 + ignore: 5.3.1 imurmurhash: 0.1.4 is-glob: 4.0.3 is-path-inside: 3.0.3 @@ -10877,142 +15003,58 @@ packages: text-table: 0.2.0 transitivePeerDependencies: - supports-color - dev: true - /espree@9.6.1: - resolution: - { - integrity: sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==, - } - engines: { node: ^12.22.0 || ^14.17.0 || >=16.0.0 } + espree@9.6.1: dependencies: - acorn: 8.10.0 - acorn-jsx: 5.3.2(acorn@8.10.0) + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) eslint-visitor-keys: 3.4.3 - dev: true - - /esprima@4.0.1: - resolution: - { - integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==, - } - engines: { node: '>=4' } - hasBin: true - dev: true - /esquery@1.5.0: - resolution: - { - integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==, - } - engines: { node: '>=0.10' } + esprima@4.0.1: {} + + esquery@1.5.0: dependencies: estraverse: 5.3.0 - dev: true - /esrecurse@4.3.0: - resolution: - { - integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==, - } - engines: { node: '>=4.0' } + esquery@1.6.0: dependencies: estraverse: 5.3.0 - /estraverse@4.3.0: - resolution: - { - integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==, - } - engines: { node: '>=4.0' } - - /estraverse@5.3.0: - resolution: - { - integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==, - } - engines: { node: '>=4.0' } - - /estree-walker@2.0.2: - resolution: - { - integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==, - } - dev: true - - /estree-walker@3.0.3: - resolution: - { - integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==, - } - dependencies: - '@types/estree': 1.0.2 - dev: true - - /esutils@2.0.3: - resolution: - { - integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==, - } - engines: { node: '>=0.10.0' } - - /etag@1.8.1: - resolution: - { - integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==, - } - engines: { node: '>= 0.6' } - dev: false - - /event-target-shim@5.0.1: - resolution: - { - integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==, - } - engines: { node: '>=6' } - requiresBuild: true - dev: false + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@4.3.0: {} + + estraverse@5.3.0: {} + + estree-walker@2.0.2: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + esutils@2.0.3: {} + + etag@1.8.1: {} + + event-target-shim@5.0.1: optional: true - /eventemitter3@5.0.1: - resolution: - { - integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==, - } - dev: true - - /events@3.3.0: - resolution: - { - integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==, - } - engines: { node: '>=0.8.x' } - - /eventsource-polyfill@0.9.6: - resolution: - { - integrity: sha512-LyMFp2oPDGhum2lMvkjqKZEwWd2/AoXyt8aoyftTBMWwPHNgU+2tdxhTHPluDxoz+z4gNj0uHAPR9nqevATMbg==, - } - dev: false - - /evp_bytestokey@1.0.3: - resolution: - { - integrity: sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==, - } + eventemitter3@5.0.1: {} + + events@3.3.0: {} + + eventsource-polyfill@0.9.6: {} + + evp_bytestokey@1.0.3: dependencies: md5.js: 1.3.5 safe-buffer: 5.2.1 - dev: false - /execa@5.1.1: - resolution: - { - integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==, - } - engines: { node: '>=10' } + execa@5.1.1: dependencies: - cross-spawn: 7.0.3 + cross-spawn: 7.0.6 get-stream: 6.0.1 human-signals: 2.1.0 is-stream: 2.0.1 @@ -11022,37 +15064,23 @@ packages: signal-exit: 3.0.7 strip-final-newline: 2.0.0 - /execa@7.2.0: - resolution: - { - integrity: sha512-UduyVP7TLB5IcAQl+OzLyLcS/l32W/GLg+AhHJ+ow40FOk2U3SAllPwR44v4vmdFwIWqpdwxxpQbF1n5ta9seA==, - } - engines: { node: ^14.18.0 || ^16.14.0 || >=18.0.0 } + execa@8.0.1: dependencies: - cross-spawn: 7.0.3 - get-stream: 6.0.1 - human-signals: 4.3.1 + cross-spawn: 7.0.6 + 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 + npm-run-path: 5.3.0 onetime: 6.0.0 - signal-exit: 3.0.7 + signal-exit: 4.1.0 strip-final-newline: 3.0.0 - dev: true - - /exit@0.1.2: - resolution: - { - integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==, - } - engines: { node: '>= 0.8.0' } - - /expand-brackets@2.1.4: - resolution: - { - integrity: sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==, - } - engines: { node: '>=0.10.0' } + + exit-x@0.2.2: {} + + exit@0.1.2: {} + + expand-brackets@2.1.4: dependencies: debug: 2.6.9 define-property: 0.2.5 @@ -11063,68 +15091,35 @@ packages: to-regex: 3.0.2 transitivePeerDependencies: - supports-color - dev: false - - /expect@27.5.1: - resolution: - { - integrity: sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - dependencies: - '@jest/types': 27.5.1 - jest-get-type: 27.5.1 - jest-matcher-utils: 27.5.1 - jest-message-util: 27.5.1 - dev: true - - /extend-shallow@2.0.1: - resolution: - { - integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==, - } - engines: { node: '>=0.10.0' } + + expect@30.2.0: + dependencies: + '@jest/expect-utils': 30.2.0 + '@jest/get-type': 30.1.0 + jest-matcher-utils: 30.2.0 + jest-message-util: 30.2.0 + jest-mock: 30.2.0 + jest-util: 30.2.0 + + extend-shallow@2.0.1: dependencies: is-extendable: 0.1.1 - /extend-shallow@3.0.2: - resolution: - { - integrity: sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==, - } - engines: { node: '>=0.10.0' } + extend-shallow@3.0.2: dependencies: assign-symbols: 1.0.0 is-extendable: 1.0.1 - dev: false - - /extend@3.0.2: - resolution: - { - integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==, - } - requiresBuild: true - dev: false + + extend@3.0.2: optional: true - /external-editor@3.1.0: - resolution: - { - integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==, - } - engines: { node: '>=4' } + external-editor@3.1.0: dependencies: chardet: 0.7.0 iconv-lite: 0.4.24 tmp: 0.0.33 - dev: false - /extglob@2.0.4: - resolution: - { - integrity: sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==, - } - engines: { node: '>=0.10.0' } + extglob@2.0.4: dependencies: array-unique: 0.3.2 define-property: 1.0.0 @@ -11136,212 +15131,109 @@ packages: to-regex: 3.0.2 transitivePeerDependencies: - supports-color - dev: false - /extract-css-chunks-webpack-plugin@4.10.0(webpack@4.47.0): - resolution: - { - integrity: sha512-D/wb/Tbexq8XMBl4uhthto25WBaHI9P8vucDdzwPtLTyVi4Rdw/aiRLSL2rHaF6jZfPAjThWXepFU9PXsdtIbA==, - } - engines: { node: '>= 6.9.0' } - peerDependencies: - webpack: ^4.4.0 || ^5.0.0 + extract-css-chunks-webpack-plugin@4.10.0(webpack@4.47.0): dependencies: loader-utils: 2.0.4 normalize-url: 1.9.1 schema-utils: 1.0.0 webpack: 4.47.0 webpack-sources: 1.4.3 - dev: false - /extract-from-css@0.4.4: - resolution: - { - integrity: sha512-41qWGBdtKp9U7sgBxAQ7vonYqSXzgW/SiAYzq4tdWSVhAShvpVCH1nyvPQgjse6EdgbW7Y7ERdT3674/lKr65A==, - } - engines: { node: '>=0.10.0', npm: '>=2.0.0' } + extract-from-css@0.4.4: dependencies: css: 2.2.4 - dev: true - /fast-deep-equal@3.1.3: - resolution: - { - integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==, - } + fast-deep-equal@3.1.3: {} - /fast-glob@3.3.1: - resolution: - { - integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==, - } - engines: { node: '>=8.6.0' } + fast-glob@3.3.1: dependencies: '@nodelib/fs.stat': 2.0.5 '@nodelib/fs.walk': 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 - micromatch: 4.0.5 + micromatch: 4.0.8 + + fast-glob@3.3.2: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 - /fast-json-stable-stringify@2.1.0: - resolution: - { - integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==, - } - - /fast-levenshtein@2.0.6: - resolution: - { - integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==, - } - dev: true - - /fast-text-encoding@1.0.6: - resolution: - { - integrity: sha512-VhXlQgj9ioXCqGstD37E/HBeqEGV/qOD/kmbVG8h5xKBYvM1L3lR1Zn4555cQ8GkYbJa8aJSipLPndE1k6zK2w==, - } - requiresBuild: true - dev: false + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fast-text-encoding@1.0.6: optional: true - /fastest-levenshtein@1.0.16: - resolution: - { - integrity: sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==, - } - engines: { node: '>= 4.9.1' } - dev: true + fast-uri@3.1.0: {} - /fastq@1.15.0: - resolution: - { - integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==, - } + fastest-levenshtein@1.0.16: {} + + fastq@1.15.0: dependencies: reusify: 1.0.4 - /faye-websocket@0.11.4: - resolution: - { - integrity: sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==, - } - engines: { node: '>=0.8.0' } + faye-websocket@0.11.4: dependencies: websocket-driver: 0.7.4 - dev: false - /fb-watchman@2.0.2: - resolution: - { - integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==, - } + fb-watchman@2.0.2: dependencies: bser: 2.1.1 - dev: true - - /figgy-pudding@3.5.2: - resolution: - { - integrity: sha512-0btnI/H8f2pavGMN8w40mlSKOfTK2SVJmBfBeVIj3kNw0swwgzyRq0d5TJVOwodFmtvpPeWPN/MCcfuWF0Ezbw==, - } - dev: false - - /figures@3.2.0: - resolution: - { - integrity: sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==, - } - engines: { node: '>=8' } + + figgy-pudding@3.5.2: {} + + figures@3.2.0: dependencies: escape-string-regexp: 1.0.5 - dev: false - /file-entry-cache@6.0.1: - resolution: - { - integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==, - } - engines: { node: ^10.12.0 || >=12.0.0 } + file-entry-cache@6.0.1: dependencies: - flat-cache: 3.1.0 - dev: true + flat-cache: 3.1.1 - /file-entry-cache@7.0.1: - resolution: - { - integrity: sha512-uLfFktPmRetVCbHe5UPuekWrQ6hENufnA46qEGbfACkK5drjTTdQYUragRgMjHldcbYG+nslUerqMPjbBSHXjQ==, - } - engines: { node: '>=12.0.0' } + file-entry-cache@7.0.1: dependencies: flat-cache: 3.1.1 - dev: true - /file-loader@6.2.0(webpack@4.47.0): - resolution: - { - integrity: sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==, - } - engines: { node: '>= 10.13.0' } - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 + file-loader@6.2.0(webpack@4.47.0): dependencies: loader-utils: 2.0.4 schema-utils: 3.3.0 webpack: 4.47.0 - dev: false - /file-loader@6.2.0(webpack@5.87.0): - resolution: - { - integrity: sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==, - } - engines: { node: '>= 10.13.0' } - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 + file-loader@6.2.0(webpack@5.104.1): dependencies: loader-utils: 2.0.4 schema-utils: 3.3.0 - webpack: 5.87.0 - dev: true - - /file-uri-to-path@1.0.0: - resolution: - { - integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==, - } - requiresBuild: true - dev: false + webpack: 5.104.1 + + file-uri-to-path@1.0.0: {} + + filelist@1.0.6: + dependencies: + minimatch: 5.1.9 optional: true - /fill-range@4.0.0: - resolution: - { - integrity: sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==, - } - engines: { node: '>=0.10.0' } + fill-range@4.0.0: dependencies: extend-shallow: 2.0.1 is-number: 3.0.0 repeat-string: 1.6.1 to-regex-range: 2.1.1 - dev: false - /fill-range@7.0.1: - resolution: - { - integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==, - } - engines: { node: '>=8' } + fill-range@7.0.1: + dependencies: + to-regex-range: 5.0.1 + + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 - /finalhandler@1.1.2: - resolution: - { - integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==, - } - engines: { node: '>= 0.8' } + finalhandler@1.1.2: dependencies: debug: 2.6.9 encodeurl: 1.0.2 @@ -11352,86 +15244,44 @@ packages: unpipe: 1.0.0 transitivePeerDependencies: - supports-color - dev: false - /find-babel-config@1.2.0: - resolution: - { - integrity: sha512-jB2CHJeqy6a820ssiqwrKMeyC6nNdmrcgkKWJWmpoxpE8RKciYJXCcXRq1h2AzCo5I5BJeN2tkGEO3hLTuePRA==, - } - engines: { node: '>=4.0.0' } + find-babel-config@1.2.0: dependencies: json5: 0.5.1 path-exists: 3.0.0 - dev: true - /find-cache-dir@2.1.0: - resolution: - { - integrity: sha512-Tq6PixE0w/VMFfCgbONnkiQIVol/JJL7nRMi20fqzA4NRs9AfeqMGeRdPi3wIhYkxjeBaWh2rxwapn5Tu3IqOQ==, - } - engines: { node: '>=6' } + find-cache-dir@2.1.0: dependencies: commondir: 1.0.1 make-dir: 2.1.0 pkg-dir: 3.0.0 - dev: false - /find-cache-dir@3.3.2: - resolution: - { - integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==, - } - engines: { node: '>=8' } + find-cache-dir@3.3.2: dependencies: commondir: 1.0.1 make-dir: 3.1.0 pkg-dir: 4.2.0 - dev: false - /find-up@3.0.0: - resolution: - { - integrity: sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==, - } - engines: { node: '>=6' } + find-up@3.0.0: dependencies: locate-path: 3.0.0 - dev: false - /find-up@4.1.0: - resolution: - { - integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==, - } - engines: { node: '>=8' } + find-up@4.1.0: dependencies: locate-path: 5.0.0 path-exists: 4.0.0 - /find-up@5.0.0: - resolution: - { - integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==, - } - engines: { node: '>=10' } + find-up@5.0.0: dependencies: locate-path: 6.0.0 path-exists: 4.0.0 - dev: true - /firebase-admin@10.3.0(@firebase/app-types@0.9.0): - resolution: - { - integrity: sha512-A0wgMLEjyVyUE+heyMJYqHRkPVjpebhOYsa47RHdrTM4ltApcx8Tn86sUmjqxlfh09gNnILAm7a8q5+FmgBYpg==, - } - engines: { node: '>=12.7.0' } - requiresBuild: true + firebase-admin@10.3.0(@firebase/app-types@0.9.2): dependencies: '@fastify/busboy': 1.2.1 - '@firebase/database-compat': 0.2.10(@firebase/app-types@0.9.0) + '@firebase/database-compat': 0.2.10(@firebase/app-types@0.9.2) '@firebase/database-types': 0.9.17 - '@types/node': 20.8.0 + '@types/node': 25.1.0 jsonwebtoken: 8.5.1 jwks-rsa: 2.1.5 node-forge: 1.3.1 @@ -11443,372 +15293,179 @@ packages: - '@firebase/app-types' - encoding - supports-color - dev: false optional: true - /firebase@9.23.0: - resolution: - { - integrity: sha512-/4lUVY0lUvBDIaeY1q6dUYhS8Sd18Qb9CgWkPZICUo9IXpJNCEagfNZXBBFCkMTTN5L5gx2Hjr27y21a9NzUcA==, - } - dependencies: - '@firebase/analytics': 0.10.0(@firebase/app@0.9.13) - '@firebase/analytics-compat': 0.2.6(@firebase/app-compat@0.2.13)(@firebase/app@0.9.13) - '@firebase/app': 0.9.13 - '@firebase/app-check': 0.8.0(@firebase/app@0.9.13) - '@firebase/app-check-compat': 0.3.7(@firebase/app-compat@0.2.13)(@firebase/app@0.9.13) - '@firebase/app-compat': 0.2.13 - '@firebase/app-types': 0.9.0 - '@firebase/auth': 0.23.2(@firebase/app@0.9.13) - '@firebase/auth-compat': 0.4.2(@firebase/app-compat@0.2.13)(@firebase/app-types@0.9.0)(@firebase/app@0.9.13) - '@firebase/database': 0.14.4 - '@firebase/database-compat': 0.3.4 - '@firebase/firestore': 3.13.0(@firebase/app@0.9.13) - '@firebase/firestore-compat': 0.3.12(@firebase/app-compat@0.2.13)(@firebase/app-types@0.9.0)(@firebase/app@0.9.13) - '@firebase/functions': 0.10.0(@firebase/app@0.9.13) - '@firebase/functions-compat': 0.3.5(@firebase/app-compat@0.2.13)(@firebase/app@0.9.13) - '@firebase/installations': 0.6.4(@firebase/app@0.9.13) - '@firebase/installations-compat': 0.2.4(@firebase/app-compat@0.2.13)(@firebase/app-types@0.9.0)(@firebase/app@0.9.13) - '@firebase/messaging': 0.12.4(@firebase/app@0.9.13) - '@firebase/messaging-compat': 0.2.4(@firebase/app-compat@0.2.13)(@firebase/app@0.9.13) - '@firebase/performance': 0.6.4(@firebase/app@0.9.13) - '@firebase/performance-compat': 0.2.4(@firebase/app-compat@0.2.13)(@firebase/app@0.9.13) - '@firebase/remote-config': 0.4.4(@firebase/app@0.9.13) - '@firebase/remote-config-compat': 0.2.4(@firebase/app-compat@0.2.13)(@firebase/app@0.9.13) - '@firebase/storage': 0.11.2(@firebase/app@0.9.13) - '@firebase/storage-compat': 0.3.2(@firebase/app-compat@0.2.13)(@firebase/app-types@0.9.0)(@firebase/app@0.9.13) - '@firebase/util': 1.9.3 + firebase@10.14.1: + dependencies: + '@firebase/analytics': 0.10.8(@firebase/app@0.10.13) + '@firebase/analytics-compat': 0.2.14(@firebase/app-compat@0.2.43)(@firebase/app@0.10.13) + '@firebase/app': 0.10.13 + '@firebase/app-check': 0.8.8(@firebase/app@0.10.13) + '@firebase/app-check-compat': 0.3.15(@firebase/app-compat@0.2.43)(@firebase/app@0.10.13) + '@firebase/app-compat': 0.2.43 + '@firebase/app-types': 0.9.2 + '@firebase/auth': 1.7.9(@firebase/app@0.10.13) + '@firebase/auth-compat': 0.5.14(@firebase/app-compat@0.2.43)(@firebase/app-types@0.9.2)(@firebase/app@0.10.13) + '@firebase/data-connect': 0.1.0(@firebase/app@0.10.13) + '@firebase/database': 1.0.8 + '@firebase/database-compat': 1.0.8 + '@firebase/firestore': 4.7.3(@firebase/app@0.10.13) + '@firebase/firestore-compat': 0.3.38(@firebase/app-compat@0.2.43)(@firebase/app-types@0.9.2)(@firebase/app@0.10.13) + '@firebase/functions': 0.11.8(@firebase/app@0.10.13) + '@firebase/functions-compat': 0.3.14(@firebase/app-compat@0.2.43)(@firebase/app@0.10.13) + '@firebase/installations': 0.6.9(@firebase/app@0.10.13) + '@firebase/installations-compat': 0.2.9(@firebase/app-compat@0.2.43)(@firebase/app-types@0.9.2)(@firebase/app@0.10.13) + '@firebase/messaging': 0.12.12(@firebase/app@0.10.13) + '@firebase/messaging-compat': 0.2.12(@firebase/app-compat@0.2.43)(@firebase/app@0.10.13) + '@firebase/performance': 0.6.9(@firebase/app@0.10.13) + '@firebase/performance-compat': 0.2.9(@firebase/app-compat@0.2.43)(@firebase/app@0.10.13) + '@firebase/remote-config': 0.4.9(@firebase/app@0.10.13) + '@firebase/remote-config-compat': 0.2.9(@firebase/app-compat@0.2.43)(@firebase/app@0.10.13) + '@firebase/storage': 0.13.2(@firebase/app@0.10.13) + '@firebase/storage-compat': 0.3.12(@firebase/app-compat@0.2.43)(@firebase/app-types@0.9.2)(@firebase/app@0.10.13) + '@firebase/util': 1.10.0 + '@firebase/vertexai-preview': 0.0.4(@firebase/app-types@0.9.2)(@firebase/app@0.10.13) transitivePeerDependencies: - - encoding - dev: false + - '@react-native-async-storage/async-storage' - /firebaseui@6.1.0(firebase@9.23.0): - resolution: - { - integrity: sha512-5WiVYVxPGMANuZKxg6KLyU1tyqIsbqf/59Zm4HrdFYwPtM5lxxB0THvgaIk4ix+hCgF0qmY89sKiktcifKzGIA==, - } - peerDependencies: - firebase: ^9.1.3 || ^10.0.0 + firebaseui@6.1.0(firebase@10.14.1): dependencies: dialog-polyfill: 0.4.10 - firebase: 9.23.0 + firebase: 10.14.1 material-design-lite: 1.3.0 - dev: false - - /flat-cache@3.1.0: - resolution: - { - integrity: sha512-OHx4Qwrrt0E4jEIcI5/Xb+f+QmJYNj2rrK8wiIdQOIrB9WrrJL8cjZvXdXuBTkkEwEqLycb5BeZDV1o2i9bTew==, - } - engines: { node: '>=12.0.0' } - dependencies: - flatted: 3.2.9 - keyv: 4.5.3 - rimraf: 3.0.2 - dev: true - /flat-cache@3.1.1: - resolution: - { - integrity: sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==, - } - engines: { node: '>=12.0.0' } + flat-cache@3.1.1: dependencies: flatted: 3.2.9 keyv: 4.5.3 rimraf: 3.0.2 - dev: true - /flat@5.0.2: - resolution: - { - integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==, - } - hasBin: true + flat@5.0.2: {} - /flatted@3.2.9: - resolution: - { - integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==, - } - dev: true + flatted@3.2.9: {} - /flush-write-stream@1.1.1: - resolution: - { - integrity: sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w==, - } + flush-write-stream@1.1.1: dependencies: inherits: 2.0.4 readable-stream: 2.3.8 - dev: false - /follow-redirects@1.15.3: - resolution: - { - integrity: sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==, - } - engines: { node: '>=4.0' } - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - dev: true + follow-redirects@1.16.0: {} - /for-each@0.3.3: - resolution: - { - integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==, - } + for-each@0.3.3: dependencies: is-callable: 1.2.7 - /for-in@1.0.2: - resolution: - { - integrity: sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==, - } - engines: { node: '>=0.10.0' } - dev: false - - /fork-ts-checker-webpack-plugin@6.5.3(eslint@8.53.0)(typescript@4.2.4)(vue-template-compiler@2.7.15)(webpack@5.87.0): - resolution: - { - integrity: sha512-SbH/l9ikmMWycd5puHJKTkZJKddF4iRLyW3DeZ08HTI7NGyLS38MXd/KGgeWumQO7YNQbW2u/NtPT2YowbPaGQ==, - } - engines: { node: '>=10', yarn: '>=1.0.0' } - peerDependencies: - eslint: '>= 6' - typescript: '>= 2.7' - vue-template-compiler: '*' - webpack: '>= 4' - peerDependenciesMeta: - eslint: - optional: true - vue-template-compiler: - optional: true + for-in@1.0.2: {} + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + fork-ts-checker-webpack-plugin@6.5.3(eslint@8.57.1)(typescript@4.9.5)(vue-template-compiler@2.7.16)(webpack@5.104.1): dependencies: '@babel/code-frame': 7.22.13 - '@types/json-schema': 7.0.13 + '@types/json-schema': 7.0.15 chalk: 4.1.2 chokidar: 3.5.3 cosmiconfig: 6.0.0 deepmerge: 4.3.1 - eslint: 8.53.0 fs-extra: 9.1.0 glob: 7.2.3 memfs: 3.5.3 minimatch: 3.1.2 schema-utils: 2.7.0 - semver: 7.5.4 + semver: 7.7.3 tapable: 1.1.3 - typescript: 4.2.4 - vue-template-compiler: 2.7.15 - webpack: 5.87.0 - dev: true - - /form-data@3.0.1: - resolution: - { - integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==, - } - engines: { node: '>= 6' } + typescript: 4.9.5 + webpack: 5.104.1 + optionalDependencies: + eslint: 8.57.1 + vue-template-compiler: 2.7.16 + + form-data@4.0.5: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 mime-types: 2.1.35 - dev: true - - /fraction.js@4.3.6: - resolution: - { - integrity: sha512-n2aZ9tNfYDwaHhvFTkhFErqOMIb8uyzSQ+vGJBjZyanAKZVbGUQ1sngfk9FdkBw7G26O7AgNjLcecLffD1c7eg==, - } - dev: false - - /fragment-cache@0.2.1: - resolution: - { - integrity: sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==, - } - engines: { node: '>=0.10.0' } + + fraction.js@4.3.7: {} + + fragment-cache@0.2.1: dependencies: map-cache: 0.2.2 - dev: false - - /fresh@0.5.2: - resolution: - { - integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==, - } - engines: { node: '>= 0.6' } - dev: false - - /from2@2.3.0: - resolution: - { - integrity: sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==, - } + + fresh@0.5.2: {} + + from2@2.3.0: dependencies: inherits: 2.0.4 readable-stream: 2.3.8 - dev: false - - /fs-extra@10.1.0: - resolution: - { - integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==, - } - engines: { node: '>=12' } - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.1.0 - universalify: 2.0.0 - dev: false - /fs-extra@11.1.1: - resolution: - { - integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==, - } - engines: { node: '>=14.14' } + fs-extra@11.2.0: dependencies: graceful-fs: 4.2.11 jsonfile: 6.1.0 universalify: 2.0.0 - dev: true - /fs-extra@8.1.0: - resolution: - { - integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==, - } - engines: { node: '>=6 <7 || >=8' } + fs-extra@8.1.0: dependencies: graceful-fs: 4.2.11 jsonfile: 4.0.0 universalify: 0.1.2 - dev: false - /fs-extra@9.1.0: - resolution: - { - integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==, - } - engines: { node: '>=10' } + fs-extra@9.1.0: dependencies: at-least-node: 1.0.0 graceful-fs: 4.2.11 jsonfile: 6.1.0 universalify: 2.0.0 - dev: true - - /fs-memo@1.2.0: - resolution: - { - integrity: sha512-YEexkCpL4j03jn5SxaMHqcO6IuWuqm8JFUYhyCep7Ao89JIYmB8xoKhK7zXXJ9cCaNXpyNH5L3QtAmoxjoHW2w==, - } - dev: false - - /fs-minipass@2.1.0: - resolution: - { - integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==, - } - engines: { node: '>= 8' } + + fs-memo@1.2.0: {} + + fs-minipass@2.1.0: dependencies: minipass: 3.3.6 - /fs-monkey@1.0.5: - resolution: - { - integrity: sha512-8uMbBjrhzW76TYgEV27Y5E//W2f/lTFmx78P2w19FZSxarhI/798APGQyuGCwmkNxgwGRhrLfvWyLBvNtuOmew==, - } + fs-monkey@1.0.5: {} - /fs-write-stream-atomic@1.0.10: - resolution: - { - integrity: sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA==, - } + fs-write-stream-atomic@1.0.10: dependencies: graceful-fs: 4.2.11 iferr: 0.1.5 imurmurhash: 0.1.4 readable-stream: 2.3.8 - dev: false - - /fs.realpath@1.0.0: - resolution: - { - integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==, - } - - /fsevents@1.2.13: - resolution: - { - integrity: sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==, - } - engines: { node: '>= 4.0' } - os: [darwin] - deprecated: The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2 - requiresBuild: true + + fs.realpath@1.0.0: {} + + fsevents@1.2.13: dependencies: bindings: 1.5.0 nan: 2.18.0 - dev: false optional: true - /fsevents@2.3.3: - resolution: - { - integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, - } - engines: { node: ^8.16.0 || ^10.6.0 || >=11.0.0 } - os: [darwin] - requiresBuild: true + fsevents@2.3.3: optional: true - /function-bind@1.1.1: - resolution: - { - integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==, - } + function-bind@1.1.1: {} + + function-bind@1.1.2: {} - /function.prototype.name@1.1.6: - resolution: - { - integrity: sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==, - } - engines: { node: '>= 0.4' } + function.prototype.name@1.1.6: dependencies: call-bind: 1.0.2 define-properties: 1.2.1 es-abstract: 1.22.2 functions-have-names: 1.2.3 - /functional-red-black-tree@1.0.1: - resolution: - { - integrity: sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==, - } - requiresBuild: true - dev: false + functional-red-black-tree@1.0.1: optional: true - /functions-have-names@1.2.3: - resolution: - { - integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==, - } + functions-have-names@1.2.3: {} - /gaxios@4.3.3: - resolution: - { - integrity: sha512-gSaYYIO1Y3wUtdfHmjDUZ8LWaxJQpiavzbF5Kq53akSzvmVg0RfyOcFDbO1KJ/KCGRFz2qG+lS81F0nkr7cRJA==, - } - engines: { node: '>=10' } - requiresBuild: true + gaxios@4.3.3: dependencies: abort-controller: 3.0.0 extend: 3.0.2 @@ -11818,203 +15475,135 @@ packages: transitivePeerDependencies: - encoding - supports-color - dev: false optional: true - /gcp-metadata@4.3.1: - resolution: - { - integrity: sha512-x850LS5N7V1F3UcV7PoupzGsyD6iVwTVvsh3tbXfkctZnBnjW5yu5z1/3k3SehF7TyoTIe78rJs02GMMy+LF+A==, - } - engines: { node: '>=10' } - requiresBuild: true + gcp-metadata@4.3.1: dependencies: gaxios: 4.3.3 json-bigint: 1.0.0 transitivePeerDependencies: - encoding - supports-color - dev: false optional: true - /gensync@1.0.0-beta.2: - resolution: - { - integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==, - } - engines: { node: '>=6.9.0' } - - /get-caller-file@2.0.5: - resolution: - { - integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==, - } - engines: { node: 6.* || 8.* || >= 10.* } - - /get-intrinsic@1.2.1: - resolution: - { - integrity: sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==, - } + gensync@1.0.0-beta.2: {} + + get-caller-file@2.0.5: {} + + get-east-asian-width@1.3.0: {} + + get-intrinsic@1.2.1: dependencies: - function-bind: 1.1.1 + function-bind: 1.1.2 has: 1.0.3 has-proto: 1.0.1 has-symbols: 1.0.3 - /get-package-type@0.1.0: - resolution: - { - integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==, - } - engines: { node: '>=8.0.0' } - dev: true + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-package-type@0.1.0: {} - /get-port-please@2.6.1: - resolution: - { - integrity: sha512-4PDSrL6+cuMM1xs6w36ZIkaKzzE0xzfVBCfebHIJ3FE8iB9oic/ECwPw3iNiD4h1AoJ5XLLBhEviFAVrZsDC5A==, - } + get-port-please@2.6.1: dependencies: fs-memo: 1.2.0 - dev: false - - /get-stream@6.0.1: - resolution: - { - integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==, - } - engines: { node: '>=10' } - - /get-symbol-description@1.0.0: - resolution: - { - integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==, - } - engines: { node: '>= 0.4' } + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-stream@6.0.1: {} + + get-stream@8.0.1: {} + + get-symbol-description@1.0.0: dependencies: call-bind: 1.0.2 - get-intrinsic: 1.2.1 + get-intrinsic: 1.3.0 - /get-tsconfig@4.7.2: - resolution: - { - integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==, - } + get-tsconfig@4.7.2: dependencies: resolve-pkg-maps: 1.0.0 - dev: true - - /get-value@2.0.6: - resolution: - { - integrity: sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==, - } - engines: { node: '>=0.10.0' } - dev: false - - /giget@1.1.2: - resolution: - { - integrity: sha512-HsLoS07HiQ5oqvObOI+Qb2tyZH4Gj5nYGfF9qQcZNrPw+uEFhdXtgJr01aO2pWadGHucajYDLxxbtQkm97ON2A==, - } - hasBin: true + + get-value@2.0.6: {} + + giget@1.1.2: dependencies: colorette: 2.0.20 - defu: 6.1.2 + defu: 6.1.4 https-proxy-agent: 5.0.1 mri: 1.2.0 - node-fetch-native: 1.4.1 - pathe: 1.1.1 + node-fetch-native: 1.6.7 + pathe: 1.1.2 tar: 6.2.0 transitivePeerDependencies: - supports-color - dev: true - - /git-config-path@2.0.0: - resolution: - { - integrity: sha512-qc8h1KIQbJpp+241id3GuAtkdyJ+IK+LIVtkiFTRKRrmddDzs3SI9CvP1QYmWBFvm1I/PWRwj//of8bgAc0ltA==, - } - engines: { node: '>=4' } - dev: false - - /git-raw-commits@2.0.11: - resolution: - { - integrity: sha512-VnctFhw+xfj8Va1xtfEqCUD2XDrbAPSJx+hSrE5K7fGdjZruW7XV+QOrN7LF/RJyvspRiD2I0asWsxFp0ya26A==, - } - engines: { node: '>=10' } - hasBin: true + + giget@1.2.3: + dependencies: + citty: 0.1.6 + consola: 3.2.3 + defu: 6.1.4 + node-fetch-native: 1.6.7 + nypm: 0.3.9 + ohash: 1.1.4 + pathe: 1.1.2 + tar: 6.2.0 + + git-config-path@2.0.0: {} + + git-raw-commits@4.0.0: dependencies: - dargs: 7.0.0 - lodash: 4.17.21 - meow: 8.1.2 - split2: 3.2.2 - through2: 4.0.2 - dev: true + dargs: 8.1.0 + meow: 12.1.1 + split2: 4.2.0 - /git-up@7.0.0: - resolution: - { - integrity: sha512-ONdIrbBCFusq1Oy0sC71F5azx8bVkvtZtMJAsv+a6lz5YAmbNnLD6HAB4gptHZVLPR8S2/kVN6Gab7lryq5+lQ==, - } + git-up@7.0.0: dependencies: is-ssh: 1.4.0 parse-url: 8.1.0 - dev: false - /git-url-parse@13.1.0: - resolution: - { - integrity: sha512-5FvPJP/70WkIprlUZ33bm4UAaFdjcLkJLpWft1BeZKqwR0uhhNGoKwlUaPtVb4LxCSQ++erHapRak9kWGj+FCA==, - } + git-url-parse@13.1.1: dependencies: git-up: 7.0.0 - dev: false - /glob-parent@3.1.0: - resolution: - { - integrity: sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==, - } - requiresBuild: true + glob-parent@3.1.0: dependencies: is-glob: 3.1.0 path-dirname: 1.0.2 - dev: false optional: true - /glob-parent@5.1.2: - resolution: - { - integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==, - } - engines: { node: '>= 6' } + glob-parent@5.1.2: dependencies: is-glob: 4.0.3 - /glob-parent@6.0.2: - resolution: - { - integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==, - } - engines: { node: '>=10.13.0' } + glob-parent@6.0.2: dependencies: is-glob: 4.0.3 - dev: true - /glob-to-regexp@0.4.1: - resolution: - { - integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==, - } + glob-to-regexp@0.4.1: {} + + glob@10.4.5: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 - /glob@7.2.3: - resolution: - { - integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==, - } + glob@7.2.3: dependencies: fs.realpath: 1.0.0 inflight: 1.0.6 @@ -12023,12 +15612,7 @@ packages: once: 1.4.0 path-is-absolute: 1.0.1 - /glob@8.1.0: - resolution: - { - integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==, - } - engines: { node: '>=12' } + glob@8.1.0: dependencies: fs.realpath: 1.0.0 inflight: 1.0.6 @@ -12036,114 +15620,61 @@ packages: minimatch: 5.1.6 once: 1.4.0 - /global-dirs@0.1.1: - resolution: - { - integrity: sha512-NknMLn7F2J7aflwFOlGdNIuCDpN3VGoSoB+aap3KABFWbHVn1TCgFC+np23J8W2BiZbjfEw3BFBycSMv1AFblg==, - } - engines: { node: '>=4' } + global-directory@4.0.1: dependencies: - ini: 1.3.8 - dev: true + ini: 4.1.1 - /global-modules@2.0.0: - resolution: - { - integrity: sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==, - } - engines: { node: '>=6' } + global-modules@2.0.0: dependencies: global-prefix: 3.0.0 - dev: true - /global-prefix@3.0.0: - resolution: - { - integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==, - } - engines: { node: '>=6' } + global-prefix@3.0.0: dependencies: ini: 1.3.8 kind-of: 6.0.3 which: 1.3.1 - dev: true - - /globals@11.12.0: - resolution: - { - integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==, - } - engines: { node: '>=4' } - - /globals@13.22.0: - resolution: - { - integrity: sha512-H1Ddc/PbZHTDVJSnj8kWptIRSD6AM3pK+mKytuIVF4uoBV7rshFlhhvA58ceJ5wp3Er58w6zj7bykMpYXt3ETw==, - } - engines: { node: '>=8' } + + globals@11.12.0: {} + + globals@13.24.0: dependencies: type-fest: 0.20.2 - dev: true - - /globals@9.18.0: - resolution: - { - integrity: sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==, - } - engines: { node: '>=0.10.0' } - dev: true - - /globalthis@1.0.3: - resolution: - { - integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==, - } - engines: { node: '>= 0.4' } + + globals@9.18.0: {} + + globalthis@1.0.3: dependencies: define-properties: 1.2.1 - /globby@11.1.0: - resolution: - { - integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==, - } - engines: { node: '>=10' } + globby@11.1.0: dependencies: array-union: 2.1.0 dir-glob: 3.0.1 fast-glob: 3.3.1 - ignore: 5.2.4 + ignore: 5.3.1 merge2: 1.4.1 slash: 3.0.0 - /globby@13.2.2: - resolution: - { - integrity: sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==, - } - engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + globby@13.2.2: dependencies: dir-glob: 3.0.1 fast-glob: 3.3.1 - ignore: 5.2.4 + ignore: 5.3.1 merge2: 1.4.1 slash: 4.0.0 - dev: true - - /globjoin@0.1.4: - resolution: - { - integrity: sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==, - } - dev: true - - /google-auth-library@7.14.1: - resolution: - { - integrity: sha512-5Rk7iLNDFhFeBYc3s8l1CqzbEBcdhwR193RlD4vSNFajIcINKI8W8P0JLmBpwymHqqWbX34pJDQu39cSy/6RsA==, - } - engines: { node: '>=10' } - requiresBuild: true + + globby@14.0.2: + dependencies: + '@sindresorhus/merge-streams': 2.3.0 + fast-glob: 3.3.2 + ignore: 5.3.1 + path-type: 5.0.0 + slash: 5.1.0 + unicorn-magic: 0.1.0 + + globjoin@0.1.4: {} + + google-auth-library@7.14.1: dependencies: arrify: 2.0.1 base64-js: 1.5.1 @@ -12157,17 +15688,9 @@ packages: transitivePeerDependencies: - encoding - supports-color - dev: false optional: true - /google-gax@2.30.5: - resolution: - { - integrity: sha512-Jey13YrAN2hfpozHzbtrwEfEHdStJh1GwaQ2+Akh1k0Tv/EuNVSuBtHZoKSBm5wBMvNsxTsEIZ/152NrYyZgxQ==, - } - engines: { node: '>=10' } - hasBin: true - requiresBuild: true + google-gax@2.30.5: dependencies: '@grpc/grpc-js': 1.6.12 '@grpc/proto-loader': 0.6.13 @@ -12185,50 +15708,24 @@ packages: transitivePeerDependencies: - encoding - supports-color - dev: false optional: true - /google-p12-pem@3.1.4: - resolution: - { - integrity: sha512-HHuHmkLgwjdmVRngf5+gSmpkyaRI6QmOg77J8tkNBHhNEI62sGHyw4/+UkgyZEI7h84NbWprXDJ+sa3xOYFvTg==, - } - engines: { node: '>=10' } - hasBin: true - requiresBuild: true + google-p12-pem@3.1.4: dependencies: node-forge: 1.3.1 - dev: false optional: true - /gopd@1.0.1: - resolution: - { - integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==, - } + gopd@1.0.1: dependencies: - get-intrinsic: 1.2.1 + get-intrinsic: 1.3.0 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + graphemer@1.4.0: {} - /graceful-fs@4.2.11: - resolution: - { - integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==, - } - - /graphemer@1.4.0: - resolution: - { - integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==, - } - dev: true - - /gtoken@5.3.2: - resolution: - { - integrity: sha512-gkvEKREW7dXWF8NV8pVrKfW7WqReAmjjkMBh6lNCCGOM4ucS0r0YyXXl0r/9Yj8wcW/32ISkfc8h5mPTDbtifQ==, - } - engines: { node: '>=10' } - requiresBuild: true + gtoken@5.3.2: dependencies: gaxios: 4.3.3 google-p12-pem: 3.1.4 @@ -12236,42 +15733,24 @@ packages: transitivePeerDependencies: - encoding - supports-color - dev: false optional: true - /gzip-size@6.0.0: - resolution: - { - integrity: sha512-ax7ZYomf6jqPTQ4+XCpUGyXKHk5WweS+e05MBO4/y3WJ5RkmPXNKvX+bx1behVILVwr6JSQvZAku021CHPXG3Q==, - } - engines: { node: '>=10' } + gzip-size@6.0.0: dependencies: duplexer: 0.1.2 - dev: false - - /hable@3.0.0: - resolution: - { - integrity: sha512-7+G0/2/COR8pwteYFqHIVYfQpuEiO2HXwJrhCBJVgrNrl9O5eaUoJVDGXUJX+0RpGncNVTuestexjk1afj01wQ==, - } - dev: false - - /hard-rejection@2.1.0: - resolution: - { - integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==, - } - engines: { node: '>=6' } - dev: true - - /hard-source-webpack-plugin@0.13.1(webpack@4.47.0): - resolution: - { - integrity: sha512-r9zf5Wq7IqJHdVAQsZ4OP+dcUSvoHqDMxJlIzaE2J0TZWn3UjMMrHqwDHR8Jr/pzPfG7XxSe36E7Y8QGNdtuAw==, - } - engines: { node: '>=8.0.0' } - peerDependencies: - webpack: '*' + + handlebars@4.7.8: + dependencies: + minimist: 1.2.8 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.19.3 + + hard-rejection@2.1.0: {} + + hard-source-webpack-plugin@0.13.1(webpack@4.47.0): dependencies: chalk: 2.4.2 find-cache-dir: 2.1.0 @@ -12287,245 +15766,105 @@ packages: webpack: 4.47.0 webpack-sources: 1.4.3 write-json-file: 2.3.0 - dev: false - /has-ansi@2.0.0: - resolution: - { - integrity: sha512-C8vBJ8DwUCx19vhm7urhTuUsr4/IyP6l4VzNQDv+ryHQObW3TTTp9yB68WpYgRe2bbaGuZ/se74IqFeVnMnLZg==, - } - engines: { node: '>=0.10.0' } + has-ansi@2.0.0: dependencies: ansi-regex: 2.1.1 - dev: true - - /has-bigints@1.0.2: - resolution: - { - integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==, - } - - /has-flag@3.0.0: - resolution: - { - integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==, - } - engines: { node: '>=4' } - - /has-flag@4.0.0: - resolution: - { - integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==, - } - engines: { node: '>=8' } - - /has-property-descriptors@1.0.0: - resolution: - { - integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==, - } + + has-bigints@1.0.2: {} + + has-flag@3.0.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.0: dependencies: - get-intrinsic: 1.2.1 + get-intrinsic: 1.3.0 + + has-proto@1.0.1: {} - /has-proto@1.0.1: - resolution: - { - integrity: sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==, - } - engines: { node: '>= 0.4' } - - /has-symbols@1.0.3: - resolution: - { - integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==, - } - engines: { node: '>= 0.4' } - - /has-tostringtag@1.0.0: - resolution: - { - integrity: sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==, - } - engines: { node: '>= 0.4' } + has-symbols@1.0.3: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: dependencies: - has-symbols: 1.0.3 + has-symbols: 1.1.0 - /has-value@0.3.1: - resolution: - { - integrity: sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==, - } - engines: { node: '>=0.10.0' } + has-value@0.3.1: dependencies: get-value: 2.0.6 has-values: 0.1.4 isobject: 2.1.0 - dev: false - /has-value@1.0.0: - resolution: - { - integrity: sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==, - } - engines: { node: '>=0.10.0' } + has-value@1.0.0: dependencies: get-value: 2.0.6 has-values: 1.0.0 isobject: 3.0.1 - dev: false - - /has-values@0.1.4: - resolution: - { - integrity: sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==, - } - engines: { node: '>=0.10.0' } - dev: false - - /has-values@1.0.0: - resolution: - { - integrity: sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==, - } - engines: { node: '>=0.10.0' } + + has-values@0.1.4: {} + + has-values@1.0.0: dependencies: is-number: 3.0.0 kind-of: 4.0.0 - dev: false - /has@1.0.3: - resolution: - { - integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==, - } - engines: { node: '>= 0.4.0' } + has@1.0.3: dependencies: function-bind: 1.1.1 - /hash-base@3.1.0: - resolution: - { - integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==, - } - engines: { node: '>=4' } + hash-base@3.1.0: dependencies: inherits: 2.0.4 readable-stream: 3.6.2 safe-buffer: 5.2.1 - dev: false - - /hash-stream-validation@0.2.4: - resolution: - { - integrity: sha512-Gjzu0Xn7IagXVkSu9cSFuK1fqzwtLwFhNhVL8IFJijRNMgUttFbBSIAzKuSIrsFMO1+g1RlsoN49zPIbwPDMGQ==, - } - requiresBuild: true - dev: false + + hash-stream-validation@0.2.4: optional: true - /hash-sum@1.0.2: - resolution: - { - integrity: sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==, - } - dev: false - - /hash-sum@2.0.0: - resolution: - { - integrity: sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==, - } - - /hash.js@1.1.7: - resolution: - { - integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==, - } + hash-sum@1.0.2: {} + + hash-sum@2.0.0: {} + + hash.js@1.1.7: dependencies: inherits: 2.0.4 minimalistic-assert: 1.0.1 - dev: false - /he@1.2.0: - resolution: - { - integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==, - } - hasBin: true + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 - /highlight.js@11.8.0: - resolution: - { - integrity: sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg==, - } - engines: { node: '>=12.0.0' } + he@1.2.0: {} - /hmac-drbg@1.0.1: - resolution: - { - integrity: sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==, - } + highlight.js@11.11.1: {} + + hmac-drbg@1.0.1: dependencies: hash.js: 1.1.7 minimalistic-assert: 1.0.1 minimalistic-crypto-utils: 1.0.1 - dev: false - - /hookable@5.5.3: - resolution: - { - integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==, - } - dev: true - - /hosted-git-info@2.8.9: - resolution: - { - integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==, - } - dev: true - - /hosted-git-info@4.1.0: - resolution: - { - integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==, - } - engines: { node: '>=10' } + + hookable@4.4.1: {} + + hookable@5.5.3: {} + + hosted-git-info@2.8.9: {} + + hosted-git-info@4.1.0: dependencies: lru-cache: 6.0.0 - dev: true - - /html-encoding-sniffer@2.0.1: - resolution: - { - integrity: sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==, - } - engines: { node: '>=10' } - dependencies: - whatwg-encoding: 1.0.5 - dev: true - - /html-entities@2.4.0: - resolution: - { - integrity: sha512-igBTJcNNNhvZFRtm8uA6xMY6xYleeDwn3PeBCkDz7tHttv4F2hsDI2aPgNERWzvRcNYHNT3ymRaQzllmXj4YsQ==, - } - dev: false - - /html-escaper@2.0.2: - resolution: - { - integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==, - } - dev: true - - /html-minifier-terser@5.1.1: - resolution: - { - integrity: sha512-ZPr5MNObqnV/T9akshPKbVgyOqLmy+Bxo7juKCfTfnjNniTAMdy4hz21YQqoofMBJD2kdREaqPPdThoR78Tgxg==, - } - engines: { node: '>=6' } - hasBin: true + + html-encoding-sniffer@4.0.0: + dependencies: + whatwg-encoding: 3.1.1 + + html-entities@2.4.0: {} + + html-escaper@2.0.2: {} + + html-minifier-terser@5.1.1: dependencies: camel-case: 4.1.2 clean-css: 4.2.4 @@ -12534,53 +15873,26 @@ packages: param-case: 3.0.4 relateurl: 0.2.7 terser: 4.8.1 - dev: false - - /html-minifier@4.0.0: - resolution: - { - integrity: sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==, - } - engines: { node: '>=6' } - hasBin: true + + html-minifier-terser@7.2.0: dependencies: - camel-case: 3.0.0 - clean-css: 4.2.4 - commander: 2.20.3 - he: 1.2.0 - param-case: 2.1.1 + camel-case: 4.1.2 + clean-css: 5.3.3 + commander: 10.0.1 + entities: 4.5.0 + param-case: 3.0.4 relateurl: 0.2.7 - uglify-js: 3.17.4 - dev: false - - /html-tags@2.0.0: - resolution: - { - integrity: sha512-+Il6N8cCo2wB/Vd3gqy/8TZhTD3QvcVeQLCnZiGkGCH3JP28IgGAY41giccp2W4R3jfyJPAP318FQTa1yU7K7g==, - } - engines: { node: '>=4' } - dev: false - - /html-tags@3.3.1: - resolution: - { - integrity: sha512-ztqyC3kLto0e9WbNp0aeP+M3kTt+nbaIveGmUxAtZa+8iFgKLUOD4YKM5j+f3QD89bra7UeumolZHKuOXnTmeQ==, - } - engines: { node: '>=8' } - dev: true - - /html-webpack-plugin@4.5.2(webpack@4.47.0): - resolution: - { - integrity: sha512-q5oYdzjKUIPQVjOosjgvCHQOv9Ett9CYYHlgvJeXG0qQvdSojnBq4vAdQBwn1+yGveAwHCoe/rMR86ozX3+c2A==, - } - engines: { node: '>=6.9' } - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 + terser: 5.44.1 + + html-tags@2.0.0: {} + + html-tags@3.3.1: {} + + html-webpack-plugin@4.5.2(webpack@4.47.0): dependencies: '@types/html-minifier-terser': 5.1.2 '@types/tapable': 1.0.9 - '@types/webpack': 4.41.34 + '@types/webpack': 4.41.38 html-minifier-terser: 5.1.1 loader-utils: 1.4.2 lodash: 4.17.21 @@ -12588,266 +15900,132 @@ packages: tapable: 1.1.3 util.promisify: 1.0.0 webpack: 4.47.0 - dev: false - /htmlparser2@6.1.0: - resolution: - { - integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==, - } + htmlparser2@6.1.0: dependencies: domelementtype: 2.3.0 domhandler: 4.3.1 domutils: 2.8.0 entities: 2.2.0 - dev: false - /htmlparser2@8.0.2: - resolution: - { - integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==, - } + htmlparser2@8.0.2: dependencies: domelementtype: 2.3.0 domhandler: 5.0.3 - domutils: 3.1.0 + domutils: 3.2.2 entities: 4.5.0 - dev: true - /http-errors@2.0.0: - resolution: - { - integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==, - } - engines: { node: '>= 0.8' } + http-errors@2.0.0: dependencies: depd: 2.0.0 inherits: 2.0.4 setprototypeof: 1.2.0 statuses: 2.0.1 toidentifier: 1.0.1 - dev: false - - /http-parser-js@0.5.8: - resolution: - { - integrity: sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==, - } - dev: false - - /http-proxy-agent@4.0.1: - resolution: - { - integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==, - } - engines: { node: '>= 6' } - dependencies: - '@tootallnate/once': 1.1.2 - agent-base: 6.0.2 - debug: 4.3.4 - transitivePeerDependencies: - - supports-color - dev: true - /http-proxy-agent@5.0.0: - resolution: - { - integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==, - } - engines: { node: '>= 6' } - requiresBuild: true + http-parser-js@0.5.8: {} + + http-proxy-agent@5.0.0: dependencies: '@tootallnate/once': 2.0.0 agent-base: 6.0.2 - debug: 4.3.4 + debug: 4.4.3 transitivePeerDependencies: - supports-color - dev: false optional: true - /https-browserify@1.0.0: - resolution: - { - integrity: sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==, - } - dev: false + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + https-browserify@1.0.0: {} - /https-proxy-agent@5.0.1: - resolution: - { - integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==, - } - engines: { node: '>= 6' } + https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.3.4 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.4 + debug: 4.4.3 transitivePeerDependencies: - supports-color - /human-signals@2.1.0: - resolution: - { - integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==, - } - engines: { node: '>=10.17.0' } - - /human-signals@4.3.1: - resolution: - { - integrity: sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==, - } - engines: { node: '>=14.18.0' } - dev: true - - /iconv-lite@0.4.24: - resolution: - { - integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==, - } - engines: { node: '>=0.10.0' } + human-signals@2.1.0: {} + + human-signals@5.0.0: {} + + hyperdyperid@1.2.0: {} + + iconv-lite@0.4.24: dependencies: safer-buffer: 2.1.2 - /icss-utils@5.1.0(postcss@8.4.31): - resolution: - { - integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==, - } - engines: { node: ^10 || ^12 || >= 14 } - peerDependencies: - postcss: ^8.1.0 + iconv-lite@0.6.3: dependencies: - postcss: 8.4.31 - dev: false - - /idb@7.0.1: - resolution: - { - integrity: sha512-UUxlE7vGWK5RfB/fDwEGgRf84DY/ieqNha6msMV99UsEMQhJ1RwbCd8AYBj3QMgnE3VZnfQvm4oKVCJTYlqIgg==, - } - dev: false - - /idb@7.1.1: - resolution: - { - integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==, - } - dev: false - - /ieee754@1.2.1: - resolution: - { - integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==, - } - dev: false - - /iferr@0.1.5: - resolution: - { - integrity: sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==, - } - dev: false - - /ignore@5.2.4: - resolution: - { - integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==, - } - engines: { node: '>= 4' } - - /import-fresh@3.3.0: - resolution: - { - integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==, - } - engines: { node: '>=6' } + safer-buffer: 2.1.2 + + icss-utils@5.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + + idb@7.1.1: {} + + ieee754@1.2.1: {} + + iferr@0.1.5: {} + + ignore@5.2.4: {} + + ignore@5.3.1: {} + + import-fresh@3.3.0: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 - /import-lazy@4.0.0: - resolution: - { - integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==, - } - engines: { node: '>=8' } - dev: true - - /import-local@3.1.0: - resolution: - { - integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==, - } - engines: { node: '>=8' } - hasBin: true + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + import-lazy@4.0.0: {} + + import-local@3.2.0: dependencies: pkg-dir: 4.2.0 resolve-cwd: 3.0.0 - dev: true - - /imurmurhash@0.1.4: - resolution: - { - integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==, - } - engines: { node: '>=0.8.19' } - - /indent-string@4.0.0: - resolution: - { - integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==, - } - engines: { node: '>=8' } - - /indent-string@5.0.0: - resolution: - { - integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==, - } - engines: { node: '>=12' } - dev: true - - /infer-owner@1.0.4: - resolution: - { - integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==, - } - dev: false - - /inflight@1.0.6: - resolution: - { - integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==, - } + + import-meta-resolve@4.2.0: {} + + imurmurhash@0.1.4: {} + + indent-string@4.0.0: {} + + indent-string@5.0.0: {} + + infer-owner@1.0.4: {} + + inflight@1.0.6: dependencies: once: 1.4.0 wrappy: 1.0.2 - /inherits@2.0.3: - resolution: - { - integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==, - } - dev: false - - /inherits@2.0.4: - resolution: - { - integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==, - } - - /ini@1.3.8: - resolution: - { - integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==, - } - - /inquirer@7.3.3: - resolution: - { - integrity: sha512-JG3eIAj5V9CwcGvuOmoo6LB9kbAYT8HXffUl6memuszlwDC/qvFAJw49XJ5NROSFNPxp3iQg1GqkFhaY/CR0IA==, - } - engines: { node: '>=8.0.0' } + inherits@2.0.3: {} + + inherits@2.0.4: {} + + ini@1.3.8: {} + + ini@4.1.1: {} + + inquirer@7.3.3: dependencies: ansi-escapes: 4.3.2 chalk: 4.1.2 @@ -12862,1445 +16040,706 @@ packages: string-width: 4.2.3 strip-ansi: 6.0.1 through: 2.3.8 - dev: false - /internal-slot@1.0.5: - resolution: - { - integrity: sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==, - } - engines: { node: '>= 0.4' } + internal-slot@1.0.5: dependencies: - get-intrinsic: 1.2.1 + get-intrinsic: 1.3.0 has: 1.0.3 side-channel: 1.0.4 - /invariant@2.2.4: - resolution: - { - integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==, - } + invariant@2.2.4: dependencies: loose-envify: 1.4.0 - dev: true - - /ip@1.1.8: - resolution: - { - integrity: sha512-PuExPYUiu6qMBQb4l06ecm6T6ujzhmh+MeJcW9wa89PoAz5pvd4zPgN5WJV104mb6S2T1AwNIAaB70JNrLQWhg==, - } - dev: false - - /is-accessor-descriptor@0.1.6: - resolution: - { - integrity: sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==, - } - engines: { node: '>=0.10.0' } + + ip@2.0.1: {} + + is-accessor-descriptor@0.1.6: dependencies: kind-of: 3.2.2 - dev: false - /is-accessor-descriptor@1.0.0: - resolution: - { - integrity: sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==, - } - engines: { node: '>=0.10.0' } + is-accessor-descriptor@1.0.0: dependencies: kind-of: 6.0.3 - dev: false - /is-array-buffer@3.0.2: - resolution: - { - integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==, - } + is-array-buffer@3.0.2: dependencies: call-bind: 1.0.2 - get-intrinsic: 1.2.1 + get-intrinsic: 1.3.0 is-typed-array: 1.1.12 - /is-arrayish@0.2.1: - resolution: - { - integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==, - } + is-arrayish@0.2.1: {} - /is-bigint@1.0.4: - resolution: - { - integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==, - } + is-bigint@1.0.4: dependencies: has-bigints: 1.0.2 - /is-binary-path@1.0.1: - resolution: - { - integrity: sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==, - } - engines: { node: '>=0.10.0' } - requiresBuild: true + is-binary-path@1.0.1: dependencies: binary-extensions: 1.13.1 - dev: false optional: true - /is-binary-path@2.1.0: - resolution: - { - integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==, - } - engines: { node: '>=8' } + is-binary-path@2.1.0: dependencies: binary-extensions: 2.2.0 - /is-boolean-object@1.1.2: - resolution: - { - integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==, - } - engines: { node: '>= 0.4' } + is-boolean-object@1.1.2: dependencies: call-bind: 1.0.2 - has-tostringtag: 1.0.0 + has-tostringtag: 1.0.2 - /is-buffer@1.1.6: - resolution: - { - integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==, - } + is-buffer@1.1.6: {} - /is-builtin-module@3.2.1: - resolution: - { - integrity: sha512-BSLE3HnV2syZ0FK0iMA/yUGplUeMmNz4AW5fnTunbCIqZi4vG3WjJT9FHMy5D69xmAYBHXQhJdALdpwVxV501A==, - } - engines: { node: '>=6' } + is-builtin-module@3.2.1: dependencies: builtin-modules: 3.3.0 - dev: true - /is-callable@1.2.7: - resolution: - { - integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==, - } - engines: { node: '>= 0.4' } + is-callable@1.2.7: {} - /is-core-module@2.13.0: - resolution: - { - integrity: sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==, - } + is-core-module@2.13.0: dependencies: has: 1.0.3 - /is-data-descriptor@0.1.4: - resolution: - { - integrity: sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==, - } - engines: { node: '>=0.10.0' } + is-data-descriptor@0.1.4: dependencies: kind-of: 3.2.2 - dev: false - /is-data-descriptor@1.0.0: - resolution: - { - integrity: sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==, - } - engines: { node: '>=0.10.0' } + is-data-descriptor@1.0.0: dependencies: kind-of: 6.0.3 - dev: false - /is-date-object@1.0.5: - resolution: - { - integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==, - } - engines: { node: '>= 0.4' } + is-date-object@1.0.5: dependencies: - has-tostringtag: 1.0.0 + has-tostringtag: 1.0.2 - /is-descriptor@0.1.6: - resolution: - { - integrity: sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==, - } - engines: { node: '>=0.10.0' } + is-descriptor@0.1.6: dependencies: is-accessor-descriptor: 0.1.6 is-data-descriptor: 0.1.4 kind-of: 5.1.0 - dev: false - /is-descriptor@1.0.2: - resolution: - { - integrity: sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==, - } - engines: { node: '>=0.10.0' } + is-descriptor@1.0.2: dependencies: is-accessor-descriptor: 1.0.0 is-data-descriptor: 1.0.0 kind-of: 6.0.3 - dev: false - - /is-extendable@0.1.1: - resolution: - { - integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==, - } - engines: { node: '>=0.10.0' } - - /is-extendable@1.0.1: - resolution: - { - integrity: sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==, - } - engines: { node: '>=0.10.0' } + + is-extendable@0.1.1: {} + + is-extendable@1.0.1: dependencies: is-plain-object: 2.0.4 - dev: false - - /is-extglob@2.1.1: - resolution: - { - integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==, - } - engines: { node: '>=0.10.0' } - - /is-fullwidth-code-point@3.0.0: - resolution: - { - integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==, - } - engines: { node: '>=8' } - - /is-fullwidth-code-point@4.0.0: - resolution: - { - integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==, - } - engines: { node: '>=12' } - dev: true - - /is-generator-fn@2.1.0: - resolution: - { - integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==, - } - engines: { node: '>=6' } - dev: true - - /is-glob@3.1.0: - resolution: - { - integrity: sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==, - } - engines: { node: '>=0.10.0' } - requiresBuild: true + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-fullwidth-code-point@4.0.0: {} + + is-fullwidth-code-point@5.0.0: + dependencies: + get-east-asian-width: 1.3.0 + + is-generator-fn@2.1.0: {} + + is-glob@3.1.0: dependencies: is-extglob: 2.1.1 - dev: false optional: true - /is-glob@4.0.3: - resolution: - { - integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==, - } - engines: { node: '>=0.10.0' } + is-glob@4.0.3: dependencies: is-extglob: 2.1.1 - /is-https@2.0.2: - resolution: - { - integrity: sha512-UfUCKVQH/6PQRCh5Qk9vNu4feLZiFmV/gr8DjbtJD0IrCRIDTA6E+d/AVFGPulI5tqK5W45fYbn1Nir1O99rFw==, - } - dev: false - - /is-negative-zero@2.0.2: - resolution: - { - integrity: sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==, - } - engines: { node: '>= 0.4' } - - /is-number-object@1.0.7: - resolution: - { - integrity: sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==, - } - engines: { node: '>= 0.4' } - dependencies: - has-tostringtag: 1.0.0 - - /is-number@3.0.0: - resolution: - { - integrity: sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==, - } - engines: { node: '>=0.10.0' } + is-https@2.0.2: {} + + is-negative-zero@2.0.2: {} + + is-number-object@1.0.7: + dependencies: + has-tostringtag: 1.0.2 + + is-number@3.0.0: dependencies: kind-of: 3.2.2 - dev: false - - /is-number@7.0.0: - resolution: - { - integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==, - } - engines: { node: '>=0.12.0' } - - /is-obj@2.0.0: - resolution: - { - integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==, - } - engines: { node: '>=8' } - - /is-path-inside@3.0.3: - resolution: - { - integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==, - } - engines: { node: '>=8' } - dev: true - - /is-plain-obj@1.1.0: - resolution: - { - integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==, - } - engines: { node: '>=0.10.0' } - - /is-plain-object@2.0.4: - resolution: - { - integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==, - } - engines: { node: '>=0.10.0' } + + is-number@7.0.0: {} + + is-obj@2.0.0: {} + + is-path-inside@3.0.3: {} + + is-plain-obj@1.1.0: {} + + is-plain-obj@4.1.0: {} + + is-plain-object@2.0.4: dependencies: isobject: 3.0.1 - dev: false - - /is-plain-object@5.0.0: - resolution: - { - integrity: sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==, - } - engines: { node: '>=0.10.0' } - - /is-potential-custom-element-name@1.0.1: - resolution: - { - integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==, - } - dev: true - - /is-regex@1.1.4: - resolution: - { - integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==, - } - engines: { node: '>= 0.4' } + + is-plain-object@5.0.0: {} + + is-potential-custom-element-name@1.0.1: {} + + is-regex@1.1.4: dependencies: call-bind: 1.0.2 - has-tostringtag: 1.0.0 + has-tostringtag: 1.0.2 - /is-shared-array-buffer@1.0.2: - resolution: - { - integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==, - } + is-shared-array-buffer@1.0.2: dependencies: call-bind: 1.0.2 - /is-ssh@1.4.0: - resolution: - { - integrity: sha512-x7+VxdxOdlV3CYpjvRLBv5Lo9OJerlYanjwFrPR9fuGPjCiNiCzFgAWpiLAohSbsnH4ZAys3SBh+hq5rJosxUQ==, - } + is-ssh@1.4.0: dependencies: protocols: 2.0.1 - dev: false - - /is-stream-ended@0.1.4: - resolution: - { - integrity: sha512-xj0XPvmr7bQFTvirqnFr50o0hQIh6ZItDqloxt5aJrR4NQsYeSsyFQERYGCAzfindAcnKjINnwEEgLx4IqVzQw==, - } - requiresBuild: true - dev: false + + is-stream-ended@0.1.4: optional: true - /is-stream@2.0.1: - resolution: - { - integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==, - } - engines: { node: '>=8' } - - /is-stream@3.0.0: - resolution: - { - integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==, - } - engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } - dev: true - - /is-string@1.0.7: - resolution: - { - integrity: sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==, - } - engines: { node: '>= 0.4' } - dependencies: - has-tostringtag: 1.0.0 - - /is-symbol@1.0.4: - resolution: - { - integrity: sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==, - } - engines: { node: '>= 0.4' } + is-stream@2.0.1: {} + + is-stream@3.0.0: {} + + is-string@1.0.7: dependencies: - has-symbols: 1.0.3 + has-tostringtag: 1.0.2 - /is-text-path@1.0.1: - resolution: - { - integrity: sha512-xFuJpne9oFz5qDaodwmmG08e3CawH/2ZV8Qqza1Ko7Sk8POWbkRdwIoAWVhqvq0XeUzANEhKo2n0IXUGBm7A/w==, - } - engines: { node: '>=0.10.0' } + is-symbol@1.0.4: dependencies: - text-extensions: 1.9.0 - dev: true + has-symbols: 1.1.0 - /is-typed-array@1.1.12: - resolution: - { - integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==, - } - engines: { node: '>= 0.4' } + is-typed-array@1.1.12: dependencies: which-typed-array: 1.1.11 - /is-typedarray@1.0.0: - resolution: - { - integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==, - } + is-typedarray@1.0.0: + optional: true - /is-weakref@1.0.2: - resolution: - { - integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==, - } + is-weakref@1.0.2: dependencies: call-bind: 1.0.2 - /is-whitespace@0.3.0: - resolution: - { - integrity: sha512-RydPhl4S6JwAyj0JJjshWJEFG6hNye3pZFBRZaTUfZFwGHxzppNaNOVgQuS/E/SlhrApuMXrpnK1EEIXfdo3Dg==, - } - engines: { node: '>=0.10.0' } - dev: true - - /is-windows@1.0.2: - resolution: - { - integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==, - } - engines: { node: '>=0.10.0' } - dev: false - - /is-wsl@1.1.0: - resolution: - { - integrity: sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==, - } - engines: { node: '>=4' } - dev: false - - /isarray@1.0.0: - resolution: - { - integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==, - } - - /isarray@2.0.5: - resolution: - { - integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==, - } - - /isexe@2.0.0: - resolution: - { - integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==, - } - - /isobject@2.1.0: - resolution: - { - integrity: sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==, - } - engines: { node: '>=0.10.0' } + is-whitespace@0.3.0: {} + + is-windows@1.0.2: {} + + is-wsl@1.1.0: {} + + isarray@1.0.0: {} + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + isobject@2.1.0: dependencies: isarray: 1.0.0 - dev: false - - /isobject@3.0.1: - resolution: - { - integrity: sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==, - } - engines: { node: '>=0.10.0' } - dev: false - - /istanbul-lib-coverage@3.2.0: - resolution: - { - integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==, - } - engines: { node: '>=8' } - dev: true - - /istanbul-lib-instrument@5.2.1: - resolution: - { - integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==, - } - engines: { node: '>=8' } - dependencies: - '@babel/core': 7.23.0 - '@babel/parser': 7.23.0 + + isobject@3.0.1: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-instrument@6.0.3: + dependencies: + '@babel/core': 7.28.4 + '@babel/parser': 7.28.4 '@istanbuljs/schema': 0.1.3 - istanbul-lib-coverage: 3.2.0 - semver: 6.3.1 + istanbul-lib-coverage: 3.2.2 + semver: 7.7.3 transitivePeerDependencies: - supports-color - dev: true - /istanbul-lib-report@3.0.1: - resolution: - { - integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==, - } - engines: { node: '>=10' } + istanbul-lib-report@3.0.1: dependencies: - istanbul-lib-coverage: 3.2.0 + istanbul-lib-coverage: 3.2.2 make-dir: 4.0.0 supports-color: 7.2.0 - dev: true - /istanbul-lib-source-maps@4.0.1: - resolution: - { - integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==, - } - engines: { node: '>=10' } + istanbul-lib-source-maps@5.0.6: dependencies: - debug: 4.3.4 - istanbul-lib-coverage: 3.2.0 - source-map: 0.6.1 + '@jridgewell/trace-mapping': 0.3.31 + debug: 4.4.3 + istanbul-lib-coverage: 3.2.2 transitivePeerDependencies: - supports-color - dev: true - /istanbul-reports@3.1.6: - resolution: - { - integrity: sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==, - } - engines: { node: '>=8' } + istanbul-reports@3.2.0: dependencies: html-escaper: 2.0.2 istanbul-lib-report: 3.0.1 - dev: true - /jest-changed-files@27.5.1: - resolution: - { - integrity: sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jake@10.9.4: + dependencies: + async: 3.2.6 + filelist: 1.0.6 + picocolors: 1.1.1 + optional: true + + jest-changed-files@30.2.0: dependencies: - '@jest/types': 27.5.1 execa: 5.1.1 - throat: 6.0.2 - dev: true - - /jest-circus@27.5.1: - resolution: - { - integrity: sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - dependencies: - '@jest/environment': 27.5.1 - '@jest/test-result': 27.5.1 - '@jest/types': 27.5.1 - '@types/node': 20.8.0 + jest-util: 30.2.0 + p-limit: 3.1.0 + + jest-circus@30.2.0: + dependencies: + '@jest/environment': 30.2.0 + '@jest/expect': 30.2.0 + '@jest/test-result': 30.2.0 + '@jest/types': 30.2.0 + '@types/node': 25.1.0 chalk: 4.1.2 co: 4.6.0 - dedent: 0.7.0 - expect: 27.5.1 + dedent: 1.7.0 is-generator-fn: 2.1.0 - jest-each: 27.5.1 - jest-matcher-utils: 27.5.1 - jest-message-util: 27.5.1 - jest-runtime: 27.5.1 - jest-snapshot: 27.5.1 - jest-util: 27.5.1 - pretty-format: 27.5.1 + jest-each: 30.2.0 + jest-matcher-utils: 30.2.0 + jest-message-util: 30.2.0 + jest-runtime: 30.2.0 + jest-snapshot: 30.2.0 + jest-util: 30.2.0 + p-limit: 3.1.0 + pretty-format: 30.2.0 + pure-rand: 7.0.1 slash: 3.0.0 stack-utils: 2.0.6 - throat: 6.0.2 transitivePeerDependencies: + - babel-plugin-macros - supports-color - dev: true - - /jest-cli@27.5.1(ts-node@10.9.1): - resolution: - { - integrity: sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true + + jest-cli@30.2.0(@types/node@25.1.0): dependencies: - '@jest/core': 27.5.1(ts-node@10.9.1) - '@jest/test-result': 27.5.1 - '@jest/types': 27.5.1 + '@jest/core': 30.2.0 + '@jest/test-result': 30.2.0 + '@jest/types': 30.2.0 chalk: 4.1.2 - exit: 0.1.2 - graceful-fs: 4.2.11 - import-local: 3.1.0 - jest-config: 27.5.1(ts-node@10.9.1) - jest-util: 27.5.1 - jest-validate: 27.5.1 - prompts: 2.4.2 - yargs: 16.2.0 + exit-x: 0.2.2 + import-local: 3.2.0 + jest-config: 30.2.0(@types/node@25.1.0) + jest-util: 30.2.0 + jest-validate: 30.2.0 + yargs: 17.7.2 transitivePeerDependencies: - - bufferutil - - canvas + - '@types/node' + - babel-plugin-macros + - esbuild-register - supports-color - ts-node - - utf-8-validate - dev: true - /jest-config@27.5.1(ts-node@10.9.1): - resolution: - { - integrity: sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - peerDependencies: - ts-node: '>=9.0.0' - peerDependenciesMeta: - ts-node: - optional: true + jest-config@30.2.0(@types/node@25.1.0): dependencies: - '@babel/core': 7.23.0 - '@jest/test-sequencer': 27.5.1 - '@jest/types': 27.5.1 - babel-jest: 27.5.1(@babel/core@7.23.0) + '@babel/core': 7.28.4 + '@jest/get-type': 30.1.0 + '@jest/pattern': 30.0.1 + '@jest/test-sequencer': 30.2.0 + '@jest/types': 30.2.0 + babel-jest: 30.2.0(@babel/core@7.28.4) chalk: 4.1.2 - ci-info: 3.8.0 + ci-info: 4.3.0 deepmerge: 4.3.1 - glob: 7.2.3 + glob: 10.4.5 graceful-fs: 4.2.11 - jest-circus: 27.5.1 - jest-environment-jsdom: 27.5.1 - jest-environment-node: 27.5.1 - jest-get-type: 27.5.1 - jest-jasmine2: 27.5.1 - jest-regex-util: 27.5.1 - jest-resolve: 27.5.1 - jest-runner: 27.5.1 - jest-util: 27.5.1 - jest-validate: 27.5.1 - micromatch: 4.0.5 + jest-circus: 30.2.0 + jest-docblock: 30.2.0 + jest-environment-node: 30.2.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.2.0 + jest-runner: 30.2.0 + jest-util: 30.2.0 + jest-validate: 30.2.0 + micromatch: 4.0.8 parse-json: 5.2.0 - pretty-format: 27.5.1 + pretty-format: 30.2.0 slash: 3.0.0 strip-json-comments: 3.1.1 - ts-node: 10.9.1(@types/node@20.5.1)(typescript@4.9.5) + optionalDependencies: + '@types/node': 25.1.0 transitivePeerDependencies: - - bufferutil - - canvas + - babel-plugin-macros - supports-color - - utf-8-validate - dev: true - /jest-diff@27.5.1: - resolution: - { - integrity: sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } + jest-diff@30.2.0: dependencies: + '@jest/diff-sequences': 30.0.1 + '@jest/get-type': 30.1.0 chalk: 4.1.2 - diff-sequences: 27.5.1 - jest-get-type: 27.5.1 - pretty-format: 27.5.1 - dev: true - - /jest-docblock@27.5.1: - resolution: - { - integrity: sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } + pretty-format: 30.2.0 + + jest-docblock@30.2.0: dependencies: detect-newline: 3.1.0 - dev: true - /jest-each@27.5.1: - resolution: - { - integrity: sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } + jest-each@30.2.0: dependencies: - '@jest/types': 27.5.1 + '@jest/get-type': 30.1.0 + '@jest/types': 30.2.0 chalk: 4.1.2 - jest-get-type: 27.5.1 - jest-util: 27.5.1 - pretty-format: 27.5.1 - dev: true - - /jest-environment-jsdom@27.5.1: - resolution: - { - integrity: sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - dependencies: - '@jest/environment': 27.5.1 - '@jest/fake-timers': 27.5.1 - '@jest/types': 27.5.1 - '@types/node': 20.8.0 - jest-mock: 27.5.1 - jest-util: 27.5.1 - jsdom: 16.7.0 + jest-util: 30.2.0 + pretty-format: 30.2.0 + + jest-environment-jsdom@30.2.0: + dependencies: + '@jest/environment': 30.2.0 + '@jest/environment-jsdom-abstract': 30.2.0(jsdom@26.1.0) + '@types/jsdom': 21.1.7 + '@types/node': 24.6.2 + jsdom: 26.1.0 transitivePeerDependencies: - bufferutil - - canvas - supports-color - utf-8-validate - dev: true - - /jest-environment-node@27.5.1: - resolution: - { - integrity: sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - dependencies: - '@jest/environment': 27.5.1 - '@jest/fake-timers': 27.5.1 - '@jest/types': 27.5.1 - '@types/node': 20.8.0 - jest-mock: 27.5.1 - jest-util: 27.5.1 - dev: true - - /jest-get-type@27.5.1: - resolution: - { - integrity: sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - dev: true - - /jest-haste-map@27.5.1: - resolution: - { - integrity: sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - dependencies: - '@jest/types': 27.5.1 - '@types/graceful-fs': 4.1.7 - '@types/node': 20.8.0 - anymatch: 3.1.3 - fb-watchman: 2.0.2 - graceful-fs: 4.2.11 - jest-regex-util: 27.5.1 - jest-serializer: 27.5.1 - jest-util: 27.5.1 - jest-worker: 27.5.1 - micromatch: 4.0.5 - walker: 1.0.8 - optionalDependencies: - fsevents: 2.3.3 - dev: true - /jest-haste-map@29.7.0: - resolution: - { - integrity: sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==, - } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + jest-environment-node@30.2.0: dependencies: - '@jest/types': 29.6.3 - '@types/graceful-fs': 4.1.7 - '@types/node': 20.8.0 + '@jest/environment': 30.2.0 + '@jest/fake-timers': 30.2.0 + '@jest/types': 30.2.0 + '@types/node': 25.1.0 + jest-mock: 30.2.0 + jest-util: 30.2.0 + jest-validate: 30.2.0 + + jest-haste-map@30.2.0: + dependencies: + '@jest/types': 30.2.0 + '@types/node': 25.1.0 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 - jest-regex-util: 29.6.3 - jest-util: 29.7.0 - jest-worker: 29.7.0 - micromatch: 4.0.5 + jest-regex-util: 30.0.1 + jest-util: 30.2.0 + jest-worker: 30.2.0 + micromatch: 4.0.8 walker: 1.0.8 optionalDependencies: fsevents: 2.3.3 - dev: true - - /jest-jasmine2@27.5.1: - resolution: - { - integrity: sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - dependencies: - '@jest/environment': 27.5.1 - '@jest/source-map': 27.5.1 - '@jest/test-result': 27.5.1 - '@jest/types': 27.5.1 - '@types/node': 20.8.0 - chalk: 4.1.2 - co: 4.6.0 - expect: 27.5.1 - is-generator-fn: 2.1.0 - jest-each: 27.5.1 - jest-matcher-utils: 27.5.1 - jest-message-util: 27.5.1 - jest-runtime: 27.5.1 - jest-snapshot: 27.5.1 - jest-util: 27.5.1 - pretty-format: 27.5.1 - throat: 6.0.2 - transitivePeerDependencies: - - supports-color - dev: true - - /jest-leak-detector@27.5.1: - resolution: - { - integrity: sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - dependencies: - jest-get-type: 27.5.1 - pretty-format: 27.5.1 - dev: true - - /jest-matcher-utils@27.5.1: - resolution: - { - integrity: sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } + + jest-leak-detector@30.2.0: + dependencies: + '@jest/get-type': 30.1.0 + pretty-format: 30.2.0 + + jest-matcher-utils@30.2.0: dependencies: + '@jest/get-type': 30.1.0 chalk: 4.1.2 - jest-diff: 27.5.1 - jest-get-type: 27.5.1 - pretty-format: 27.5.1 - dev: true - - /jest-message-util@27.5.1: - resolution: - { - integrity: sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } + jest-diff: 30.2.0 + pretty-format: 30.2.0 + + jest-message-util@30.2.0: dependencies: - '@babel/code-frame': 7.22.13 - '@jest/types': 27.5.1 - '@types/stack-utils': 2.0.1 + '@babel/code-frame': 7.27.1 + '@jest/types': 30.2.0 + '@types/stack-utils': 2.0.3 chalk: 4.1.2 graceful-fs: 4.2.11 - micromatch: 4.0.5 - pretty-format: 27.5.1 + micromatch: 4.0.8 + pretty-format: 30.2.0 slash: 3.0.0 stack-utils: 2.0.6 - dev: true - - /jest-mock@27.5.1: - resolution: - { - integrity: sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - dependencies: - '@jest/types': 27.5.1 - '@types/node': 20.8.0 - dev: true - - /jest-pnp-resolver@1.2.3(jest-resolve@27.5.1): - resolution: - { - integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==, - } - engines: { node: '>=6' } - peerDependencies: - jest-resolve: '*' - peerDependenciesMeta: - jest-resolve: - optional: true + + jest-mock@30.2.0: + dependencies: + '@jest/types': 30.2.0 + '@types/node': 25.1.0 + jest-util: 30.2.0 + + jest-pnp-resolver@1.2.3(jest-resolve@30.2.0): + optionalDependencies: + jest-resolve: 30.2.0 + + jest-regex-util@30.0.1: {} + + jest-resolve-dependencies@30.2.0: dependencies: - jest-resolve: 27.5.1 - dev: true - - /jest-regex-util@27.5.1: - resolution: - { - integrity: sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - dev: true - - /jest-regex-util@29.6.3: - resolution: - { - integrity: sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==, - } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } - dev: true - - /jest-resolve-dependencies@27.5.1: - resolution: - { - integrity: sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - dependencies: - '@jest/types': 27.5.1 - jest-regex-util: 27.5.1 - jest-snapshot: 27.5.1 + jest-regex-util: 30.0.1 + jest-snapshot: 30.2.0 transitivePeerDependencies: - supports-color - dev: true - /jest-resolve@27.5.1: - resolution: - { - integrity: sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } + jest-resolve@30.2.0: dependencies: - '@jest/types': 27.5.1 chalk: 4.1.2 graceful-fs: 4.2.11 - jest-haste-map: 27.5.1 - jest-pnp-resolver: 1.2.3(jest-resolve@27.5.1) - jest-util: 27.5.1 - jest-validate: 27.5.1 - resolve: 1.22.6 - resolve.exports: 1.1.1 + jest-haste-map: 30.2.0 + jest-pnp-resolver: 1.2.3(jest-resolve@30.2.0) + jest-util: 30.2.0 + jest-validate: 30.2.0 slash: 3.0.0 - dev: true - - /jest-runner@27.5.1: - resolution: - { - integrity: sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - dependencies: - '@jest/console': 27.5.1 - '@jest/environment': 27.5.1 - '@jest/test-result': 27.5.1 - '@jest/transform': 27.5.1 - '@jest/types': 27.5.1 - '@types/node': 20.8.0 + unrs-resolver: 1.11.1 + + jest-runner@30.2.0: + dependencies: + '@jest/console': 30.2.0 + '@jest/environment': 30.2.0 + '@jest/test-result': 30.2.0 + '@jest/transform': 30.2.0 + '@jest/types': 30.2.0 + '@types/node': 25.1.0 chalk: 4.1.2 - emittery: 0.8.1 + emittery: 0.13.1 + exit-x: 0.2.2 graceful-fs: 4.2.11 - jest-docblock: 27.5.1 - jest-environment-jsdom: 27.5.1 - jest-environment-node: 27.5.1 - jest-haste-map: 27.5.1 - jest-leak-detector: 27.5.1 - jest-message-util: 27.5.1 - jest-resolve: 27.5.1 - jest-runtime: 27.5.1 - jest-util: 27.5.1 - jest-worker: 27.5.1 - source-map-support: 0.5.21 - throat: 6.0.2 + jest-docblock: 30.2.0 + jest-environment-node: 30.2.0 + jest-haste-map: 30.2.0 + jest-leak-detector: 30.2.0 + jest-message-util: 30.2.0 + jest-resolve: 30.2.0 + jest-runtime: 30.2.0 + jest-util: 30.2.0 + jest-watcher: 30.2.0 + jest-worker: 30.2.0 + p-limit: 3.1.0 + source-map-support: 0.5.13 transitivePeerDependencies: - - bufferutil - - canvas - supports-color - - utf-8-validate - dev: true - - /jest-runtime@27.5.1: - resolution: - { - integrity: sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - dependencies: - '@jest/environment': 27.5.1 - '@jest/fake-timers': 27.5.1 - '@jest/globals': 27.5.1 - '@jest/source-map': 27.5.1 - '@jest/test-result': 27.5.1 - '@jest/transform': 27.5.1 - '@jest/types': 27.5.1 + + jest-runtime@30.2.0: + dependencies: + '@jest/environment': 30.2.0 + '@jest/fake-timers': 30.2.0 + '@jest/globals': 30.2.0 + '@jest/source-map': 30.0.1 + '@jest/test-result': 30.2.0 + '@jest/transform': 30.2.0 + '@jest/types': 30.2.0 + '@types/node': 25.1.0 chalk: 4.1.2 - cjs-module-lexer: 1.2.3 + cjs-module-lexer: 2.1.0 collect-v8-coverage: 1.0.2 - execa: 5.1.1 - glob: 7.2.3 + glob: 10.4.5 graceful-fs: 4.2.11 - jest-haste-map: 27.5.1 - jest-message-util: 27.5.1 - jest-mock: 27.5.1 - jest-regex-util: 27.5.1 - jest-resolve: 27.5.1 - jest-snapshot: 27.5.1 - jest-util: 27.5.1 + jest-haste-map: 30.2.0 + jest-message-util: 30.2.0 + jest-mock: 30.2.0 + jest-regex-util: 30.0.1 + jest-resolve: 30.2.0 + jest-snapshot: 30.2.0 + jest-util: 30.2.0 slash: 3.0.0 strip-bom: 4.0.0 transitivePeerDependencies: - supports-color - dev: true - /jest-serializer@27.5.1: - resolution: - { - integrity: sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - dependencies: - '@types/node': 20.8.0 - graceful-fs: 4.2.11 - dev: true - - /jest-snapshot@27.5.1: - resolution: - { - integrity: sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - dependencies: - '@babel/core': 7.23.0 - '@babel/generator': 7.23.0 - '@babel/plugin-syntax-typescript': 7.22.5(@babel/core@7.23.0) - '@babel/traverse': 7.23.2 - '@babel/types': 7.23.0 - '@jest/transform': 27.5.1 - '@jest/types': 27.5.1 - '@types/babel__traverse': 7.20.2 - '@types/prettier': 2.7.3 - babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.0) + jest-snapshot@30.2.0: + dependencies: + '@babel/core': 7.28.4 + '@babel/generator': 7.28.3 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4) + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.4) + '@babel/types': 7.28.4 + '@jest/expect-utils': 30.2.0 + '@jest/get-type': 30.1.0 + '@jest/snapshot-utils': 30.2.0 + '@jest/transform': 30.2.0 + '@jest/types': 30.2.0 + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.28.4) chalk: 4.1.2 - expect: 27.5.1 + expect: 30.2.0 graceful-fs: 4.2.11 - jest-diff: 27.5.1 - jest-get-type: 27.5.1 - jest-haste-map: 27.5.1 - jest-matcher-utils: 27.5.1 - jest-message-util: 27.5.1 - jest-util: 27.5.1 - natural-compare: 1.4.0 - pretty-format: 27.5.1 - semver: 7.5.4 + jest-diff: 30.2.0 + jest-matcher-utils: 30.2.0 + jest-message-util: 30.2.0 + jest-util: 30.2.0 + pretty-format: 30.2.0 + semver: 7.7.3 + synckit: 0.11.11 transitivePeerDependencies: - supports-color - dev: true - /jest-util@27.5.1: - resolution: - { - integrity: sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } + jest-util@29.7.0: dependencies: - '@jest/types': 27.5.1 - '@types/node': 20.8.0 + '@jest/types': 29.6.3 + '@types/node': 25.1.0 chalk: 4.1.2 - ci-info: 3.8.0 + ci-info: 3.9.0 graceful-fs: 4.2.11 picomatch: 2.3.1 - dev: true - /jest-util@29.7.0: - resolution: - { - integrity: sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==, - } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + jest-util@30.2.0: dependencies: - '@jest/types': 29.6.3 - '@types/node': 20.8.0 + '@jest/types': 30.2.0 + '@types/node': 25.1.0 chalk: 4.1.2 - ci-info: 3.8.0 + ci-info: 4.3.0 graceful-fs: 4.2.11 - picomatch: 2.3.1 - dev: true + picomatch: 4.0.3 - /jest-validate@27.5.1: - resolution: - { - integrity: sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } + jest-validate@30.2.0: dependencies: - '@jest/types': 27.5.1 + '@jest/get-type': 30.1.0 + '@jest/types': 30.2.0 camelcase: 6.3.0 chalk: 4.1.2 - jest-get-type: 27.5.1 leven: 3.1.0 - pretty-format: 27.5.1 - dev: true - - /jest-watcher@27.5.1: - resolution: - { - integrity: sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - dependencies: - '@jest/test-result': 27.5.1 - '@jest/types': 27.5.1 - '@types/node': 20.8.0 + pretty-format: 30.2.0 + + jest-watcher@30.2.0: + dependencies: + '@jest/test-result': 30.2.0 + '@jest/types': 30.2.0 + '@types/node': 25.1.0 ansi-escapes: 4.3.2 chalk: 4.1.2 - jest-util: 27.5.1 + emittery: 0.13.1 + jest-util: 30.2.0 string-length: 4.0.2 - dev: true - /jest-worker@26.6.2: - resolution: - { - integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==, - } - engines: { node: '>= 10.13.0' } + jest-worker@26.6.2: dependencies: - '@types/node': 20.8.0 + '@types/node': 25.1.0 merge-stream: 2.0.0 supports-color: 7.2.0 - dev: false - /jest-worker@27.5.1: - resolution: - { - integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==, - } - engines: { node: '>= 10.13.0' } + jest-worker@27.5.1: dependencies: - '@types/node': 20.8.0 + '@types/node': 25.1.0 merge-stream: 2.0.0 supports-color: 8.1.1 - /jest-worker@29.7.0: - resolution: - { - integrity: sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==, - } - engines: { node: ^14.15.0 || ^16.10.0 || >=18.0.0 } + jest-worker@29.7.0: dependencies: - '@types/node': 20.8.0 + '@types/node': 25.1.0 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 - dev: true - - /jest@27.4.4(ts-node@10.9.1): - resolution: - { - integrity: sha512-AXwEIFa58Uf1Jno3/KSo5HZZ0/2Xwqvfrz0/3bmTwImkFlbOvz5vARAW9nTrxRLkojjkitaZ1KNKAtw3JRFAaA==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - hasBin: true - peerDependencies: - node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0 - peerDependenciesMeta: - node-notifier: - optional: true + + jest-worker@30.2.0: dependencies: - '@jest/core': 27.5.1(ts-node@10.9.1) - import-local: 3.1.0 - jest-cli: 27.5.1(ts-node@10.9.1) + '@types/node': 25.1.0 + '@ungap/structured-clone': 1.3.0 + jest-util: 30.2.0 + merge-stream: 2.0.0 + supports-color: 8.1.1 + + jest@30.2.0(@types/node@25.1.0): + dependencies: + '@jest/core': 30.2.0 + '@jest/types': 30.2.0 + import-local: 3.2.0 + jest-cli: 30.2.0(@types/node@25.1.0) transitivePeerDependencies: - - bufferutil - - canvas + - '@types/node' + - babel-plugin-macros + - esbuild-register - supports-color - ts-node - - utf-8-validate - dev: true - /jiti@1.20.0: - resolution: - { - integrity: sha512-3TV69ZbrvV6U5DfQimop50jE9Dl6J8O1ja1dvBbMba/sZ3YBEQqJ2VZRoQPVnhlzjNtU1vaXRZVrVjU4qtm8yA==, - } - hasBin: true + jiti@1.20.0: {} + + jiti@1.21.0: {} - /jose@2.0.6: - resolution: - { - integrity: sha512-FVoPY7SflDodE4lknJmbAHSUjLCzE2H1F6MS0RYKMQ8SR+lNccpMf8R4eqkNYyyUjR5qZReOzZo5C5YiHOCjjg==, - } - engines: { node: '>=10.13.0 < 13 || >=13.7.0' } - requiresBuild: true + jiti@1.21.6: {} + + jiti@2.6.1: {} + + jose@2.0.7: dependencies: '@panva/asn1.js': 1.0.0 - dev: false optional: true - /js-beautify@1.14.9: - resolution: - { - integrity: sha512-coM7xq1syLcMyuVGyToxcj2AlzhkDjmfklL8r0JgJ7A76wyGMpJ1oA35mr4APdYNO/o/4YY8H54NQIJzhMbhBg==, - } - engines: { node: '>=12' } - hasBin: true + js-beautify@1.14.9: dependencies: config-chain: 1.1.13 editorconfig: 1.0.4 glob: 8.1.0 nopt: 6.0.0 - dev: true - - /js-tokens@3.0.2: - resolution: - { - integrity: sha512-RjTcuD4xjtthQkaWH7dFlH85L+QaVtSoOyGdZ3g6HFhS9dFNDfLyqgm2NFe2X6cQpeFmt0452FJjFG5UameExg==, - } - dev: true - - /js-tokens@4.0.0: - resolution: - { - integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==, - } - - /js-tokens@8.0.2: - resolution: - { - integrity: sha512-Olnt+V7xYdvGze9YTbGFZIfQXuGV4R3nQwwl8BrtgaPE/wq8UFpUHWuTNc05saowhSr1ZO6tx+V6RjE9D5YQog==, - } - dev: true - - /js-yaml@3.14.1: - resolution: - { - integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==, - } - hasBin: true + + js-tokens@3.0.2: {} + + js-tokens@4.0.0: {} + + js-tokens@9.0.1: {} + + js-yaml@3.14.1: dependencies: argparse: 1.0.10 esprima: 4.0.1 - dev: true - /js-yaml@4.1.0: - resolution: - { - integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==, - } - hasBin: true + js-yaml@4.1.0: dependencies: argparse: 2.0.1 - dev: true - /jsdom@16.7.0: - resolution: - { - integrity: sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==, - } - engines: { node: '>=10' } - peerDependencies: - canvas: ^2.5.0 - peerDependenciesMeta: - canvas: - optional: true + js-yaml@4.1.1: dependencies: - abab: 2.0.6 - acorn: 8.10.0 - acorn-globals: 6.0.0 - cssom: 0.4.4 - cssstyle: 2.3.0 - data-urls: 2.0.0 - decimal.js: 10.4.3 - domexception: 2.0.1 - escodegen: 2.1.0 - form-data: 3.0.1 - html-encoding-sniffer: 2.0.1 - http-proxy-agent: 4.0.1 - https-proxy-agent: 5.0.1 + argparse: 2.0.1 + + jsdom@26.1.0: + dependencies: + cssstyle: 4.6.0 + data-urls: 5.0.0 + decimal.js: 10.6.0 + html-encoding-sniffer: 4.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 is-potential-custom-element-name: 1.0.1 - nwsapi: 2.2.7 - parse5: 6.0.1 - saxes: 5.0.1 + nwsapi: 2.2.22 + parse5: 7.3.0 + rrweb-cssom: 0.8.0 + saxes: 6.0.0 symbol-tree: 3.2.4 - tough-cookie: 4.1.3 - w3c-hr-time: 1.0.2 - w3c-xmlserializer: 2.0.0 - webidl-conversions: 6.1.0 - whatwg-encoding: 1.0.5 - whatwg-mimetype: 2.3.0 - whatwg-url: 8.7.0 - ws: 7.5.9 - xml-name-validator: 3.0.0 + tough-cookie: 5.1.2 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.2.0 + ws: 8.18.3 + xml-name-validator: 5.0.0 transitivePeerDependencies: - bufferutil - supports-color - utf-8-validate - dev: true - /jsesc@0.5.0: - resolution: - { - integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==, - } - hasBin: true - dev: false - - /jsesc@2.5.2: - resolution: - { - integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==, - } - engines: { node: '>=4' } - hasBin: true + jsesc@0.5.0: {} + + jsesc@2.5.2: {} - /json-bigint@1.0.0: - resolution: - { - integrity: sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==, - } - requiresBuild: true + jsesc@3.1.0: {} + + json-bigint@1.0.0: dependencies: bignumber.js: 9.1.2 - dev: false optional: true - /json-buffer@3.0.1: - resolution: - { - integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==, - } - dev: true - - /json-parse-better-errors@1.0.2: - resolution: - { - integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==, - } - dev: false - - /json-parse-even-better-errors@2.3.1: - resolution: - { - integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==, - } - - /json-schema-traverse@0.4.1: - resolution: - { - integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==, - } - - /json-schema-traverse@1.0.0: - resolution: - { - integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==, - } - - /json-stable-stringify-without-jsonify@1.0.1: - resolution: - { - integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==, - } - dev: true - - /json5@0.5.1: - resolution: - { - integrity: sha512-4xrs1aW+6N5DalkqSVA8fxh458CXvR99WU8WLKmq4v8eWAL86Xo3BVqyd3SkA9wEVjCMqyvvRRkshAdOnBp5rw==, - } - hasBin: true - dev: true + json-buffer@3.0.1: {} - /json5@1.0.2: - resolution: - { - integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==, - } - hasBin: true + json-parse-better-errors@1.0.2: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-traverse@0.4.1: {} + + json-schema-traverse@1.0.0: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@0.5.1: {} + + json5@1.0.2: dependencies: minimist: 1.2.8 - /json5@2.2.3: - resolution: - { - integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==, - } - engines: { node: '>=6' } - hasBin: true + json5@2.2.3: {} - /jsonc-parser@3.2.0: - resolution: - { - integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==, - } - dev: true - - /jsonfile@4.0.0: - resolution: - { - integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==, - } + jsonfile@4.0.0: optionalDependencies: graceful-fs: 4.2.11 - dev: false - /jsonfile@6.1.0: - resolution: - { - integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==, - } + jsonfile@6.1.0: dependencies: universalify: 2.0.0 optionalDependencies: graceful-fs: 4.2.11 - /jsonparse@1.3.1: - resolution: - { - integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==, - } - engines: { '0': node >= 0.2.0 } - dev: true - - /jsonwebtoken@8.5.1: - resolution: - { - integrity: sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w==, - } - engines: { node: '>=4', npm: '>=1.4.28' } - requiresBuild: true + jsonwebtoken@8.5.1: dependencies: jws: 3.2.2 lodash.includes: 4.3.0 @@ -14312,856 +16751,352 @@ packages: lodash.once: 4.1.1 ms: 2.1.3 semver: 5.7.2 - dev: false optional: true - /jwa@1.4.1: - resolution: - { - integrity: sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==, - } - requiresBuild: true + jwa@1.4.1: dependencies: buffer-equal-constant-time: 1.0.1 ecdsa-sig-formatter: 1.0.11 safe-buffer: 5.2.1 - dev: false optional: true - /jwa@2.0.0: - resolution: - { - integrity: sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==, - } - requiresBuild: true + jwa@2.0.0: dependencies: buffer-equal-constant-time: 1.0.1 ecdsa-sig-formatter: 1.0.11 safe-buffer: 5.2.1 - dev: false optional: true - /jwks-rsa@2.1.5: - resolution: - { - integrity: sha512-IODtn1SwEm7n6GQZnQLY0oxKDrMh7n/jRH1MzE8mlxWMrh2NnMyOsXTebu8vJ1qCpmuTJcL4DdiE0E4h8jnwsA==, - } - engines: { node: '>=10 < 13 || >=14' } - requiresBuild: true + jwks-rsa@2.1.5: dependencies: '@types/express': 4.17.18 '@types/jsonwebtoken': 8.5.9 - debug: 4.3.4 - jose: 2.0.6 + debug: 4.4.1 + jose: 2.0.7 limiter: 1.1.5 lru-memoizer: 2.2.0 transitivePeerDependencies: - supports-color - dev: false optional: true - /jws@3.2.2: - resolution: - { - integrity: sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==, - } - requiresBuild: true + jws@3.2.2: dependencies: jwa: 1.4.1 safe-buffer: 5.2.1 - dev: false optional: true - /jws@4.0.0: - resolution: - { - integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==, - } - requiresBuild: true + jws@4.0.0: dependencies: jwa: 2.0.0 safe-buffer: 5.2.1 - dev: false optional: true - /keyv@4.5.3: - resolution: - { - integrity: sha512-QCiSav9WaX1PgETJ+SpNnx2PRRapJ/oRSXM4VO5OGYGSjrxbKPVFVhB3l2OCbLCk329N8qyAtsJjSjvVBWzEug==, - } + kasi@2.0.1: {} + + keyv@4.5.3: dependencies: json-buffer: 3.0.1 - dev: true - /kind-of@3.2.2: - resolution: - { - integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==, - } - engines: { node: '>=0.10.0' } + kind-of@3.2.2: dependencies: is-buffer: 1.1.6 - /kind-of@4.0.0: - resolution: - { - integrity: sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==, - } - engines: { node: '>=0.10.0' } + kind-of@4.0.0: dependencies: is-buffer: 1.1.6 - dev: false - - /kind-of@5.1.0: - resolution: - { - integrity: sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==, - } - engines: { node: '>=0.10.0' } - dev: false - - /kind-of@6.0.3: - resolution: - { - integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==, - } - engines: { node: '>=0.10.0' } - - /kleur@3.0.3: - resolution: - { - integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==, - } - engines: { node: '>=6' } - dev: true - - /klona@2.0.6: - resolution: - { - integrity: sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA==, - } - engines: { node: '>= 8' } - - /knitwork@1.0.0: - resolution: - { - integrity: sha512-dWl0Dbjm6Xm+kDxhPQJsCBTxrJzuGl0aP9rhr+TG8D3l+GL90N8O8lYUi7dTSAN2uuDqCtNgb6aEuQH5wsiV8Q==, - } - dev: true - - /known-css-properties@0.29.0: - resolution: - { - integrity: sha512-Ne7wqW7/9Cz54PDt4I3tcV+hAyat8ypyOGzYRJQfdxnnjeWsTxt1cy8pjvvKeI5kfXuyvULyeeAvwvvtAX3ayQ==, - } - dev: true - - /last-call-webpack-plugin@3.0.0: - resolution: - { - integrity: sha512-7KI2l2GIZa9p2spzPIVZBYyNKkN+e/SQPpnjlTiPhdbDW3F86tdKKELxKpzJ5sgU19wQWsACULZmpTPYHeWO5w==, - } + + kind-of@5.1.0: {} + + kind-of@6.0.3: {} + + klona@2.0.6: {} + + knitwork@1.0.0: {} + + knitwork@1.1.0: {} + + known-css-properties@0.29.0: {} + + last-call-webpack-plugin@3.0.0: dependencies: lodash: 4.17.21 webpack-sources: 1.4.3 - dev: false - /launch-editor-middleware@2.6.1: - resolution: - { - integrity: sha512-Fg/xYhf7ARmRp40n18wIfJyuAMEjXo67Yull7uF7d0OJ3qA4EYJISt1XfPPn69IIJ5jKgQwzcg6DqHYo95LL/g==, - } + launch-editor-middleware@2.8.0: dependencies: - launch-editor: 2.6.1 - dev: false + launch-editor: 2.8.0 - /launch-editor@2.6.1: - resolution: - { - integrity: sha512-eB/uXmFVpY4zezmGp5XtU21kwo7GBbKB+EQ+UZeWtGb9yAM5xt/Evk+lYH3eRNAtId+ej4u7TYPFZ07w4s7rRw==, - } + launch-editor@2.8.0: dependencies: - picocolors: 1.0.0 + picocolors: 1.1.1 shell-quote: 1.8.1 - dev: false - - /leven@3.1.0: - resolution: - { - integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==, - } - engines: { node: '>=6' } - dev: true - - /levn@0.4.1: - resolution: - { - integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==, - } - engines: { node: '>= 0.8.0' } + + leven@3.1.0: {} + + levn@0.4.1: dependencies: prelude-ls: 1.2.1 type-check: 0.4.0 - dev: true - - /libphonenumber-js@1.10.49: - resolution: - { - integrity: sha512-gvLtyC3tIuqfPzjvYLH9BmVdqzGDiSi4VjtWe2fAgSdBf0yt8yPmbNnRIHNbR5IdtVkm0ayGuzwQKTWmU0hdjQ==, - } - dev: false - - /lilconfig@2.1.0: - resolution: - { - integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==, - } - engines: { node: '>=10' } - - /limiter@1.1.5: - resolution: - { - integrity: sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA==, - } - requiresBuild: true - dev: false + + libphonenumber-js@1.12.36: {} + + lilconfig@2.1.0: {} + + lilconfig@3.1.3: {} + + limiter@1.1.5: optional: true - /lines-and-columns@1.2.4: - resolution: - { - integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==, - } - - /lint-staged@14.0.1: - resolution: - { - integrity: sha512-Mw0cL6HXnHN1ag0mN/Dg4g6sr8uf8sn98w2Oc1ECtFto9tvRF7nkXGJRbx8gPlHyoR0pLyBr2lQHbWwmUHe1Sw==, - } - engines: { node: ^16.14.0 || >=18.0.0 } - hasBin: true + lines-and-columns@1.2.4: {} + + lint-staged@16.1.4: dependencies: - chalk: 5.3.0 - commander: 11.0.0 - debug: 4.3.4 - execa: 7.2.0 - lilconfig: 2.1.0 - listr2: 6.6.1 - micromatch: 4.0.5 + chalk: 5.5.0 + commander: 14.0.0 + debug: 4.4.1 + lilconfig: 3.1.3 + listr2: 9.0.1 + micromatch: 4.0.8 + nano-spawn: 1.0.2 pidtree: 0.6.0 string-argv: 0.3.2 - yaml: 2.3.1 + yaml: 2.8.1 transitivePeerDependencies: - - enquirer - supports-color - dev: true - /listr2@6.6.1: - resolution: - { - integrity: sha512-+rAXGHh0fkEWdXBmX+L6mmfmXmXvDGEKzkjxO+8mP3+nI/r/CWznVBvsibXdxda9Zz0OW2e2ikphN3OwCT/jSg==, - } - engines: { node: '>=16.0.0' } - peerDependencies: - enquirer: '>= 2.3.0 < 3' - peerDependenciesMeta: - enquirer: - optional: true + listr2@9.0.1: dependencies: - cli-truncate: 3.1.0 + cli-truncate: 4.0.0 colorette: 2.0.20 eventemitter3: 5.0.1 - log-update: 5.0.1 - rfdc: 1.3.0 - wrap-ansi: 8.1.0 - dev: true - - /loader-runner@2.4.0: - resolution: - { - integrity: sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==, - } - engines: { node: '>=4.3.0 <5.0.0 || >=5.10' } - dev: false - - /loader-runner@4.3.0: - resolution: - { - integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==, - } - engines: { node: '>=6.11.5' } - - /loader-utils@1.4.2: - resolution: - { - integrity: sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==, - } - engines: { node: '>=4.0.0' } + log-update: 6.1.0 + rfdc: 1.4.1 + wrap-ansi: 9.0.0 + + loader-runner@2.4.0: {} + + loader-runner@4.3.1: {} + + loader-utils@1.4.2: dependencies: big.js: 5.2.2 emojis-list: 3.0.0 json5: 1.0.2 - dev: false - /loader-utils@2.0.4: - resolution: - { - integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==, - } - engines: { node: '>=8.9.0' } + loader-utils@2.0.4: dependencies: big.js: 5.2.2 emojis-list: 3.0.0 json5: 2.2.3 - /local-pkg@0.4.3: - resolution: - { - integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==, - } - engines: { node: '>=14' } - dev: true - - /locate-path@3.0.0: - resolution: - { - integrity: sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==, - } - engines: { node: '>=6' } + local-pkg@0.4.3: {} + + local-pkg@0.5.0: + dependencies: + mlly: 1.7.1 + pkg-types: 1.1.2 + + locate-path@3.0.0: dependencies: p-locate: 3.0.0 path-exists: 3.0.0 - dev: false - /locate-path@5.0.0: - resolution: - { - integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==, - } - engines: { node: '>=8' } + locate-path@5.0.0: dependencies: p-locate: 4.1.0 - /locate-path@6.0.0: - resolution: - { - integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==, - } - engines: { node: '>=10' } + locate-path@6.0.0: dependencies: p-locate: 5.0.0 - dev: true - - /lodash._reinterpolate@3.0.0: - resolution: - { - integrity: sha512-xYHt68QRoYGjeeM/XOE1uJtvXQAgvszfBhjV4yvsQH0u2i9I6cI6c6/eG4Hh3UAOVn0y/xAXwmTzEay49Q//HA==, - } - dev: false - - /lodash.camelcase@4.3.0: - resolution: - { - integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==, - } - - /lodash.clonedeep@4.5.0: - resolution: - { - integrity: sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==, - } - requiresBuild: true - dev: false + + lodash._reinterpolate@3.0.0: {} + + lodash.camelcase@4.3.0: {} + + lodash.clonedeep@4.5.0: optional: true - /lodash.debounce@4.0.8: - resolution: - { - integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==, - } - dev: false - - /lodash.escape@4.0.1: - resolution: - { - integrity: sha512-nXEOnb/jK9g0DYMr1/Xvq6l5xMD7GDG55+GSYIYmS0G4tBk/hURD4JR9WCavs04t33WmJx9kCyp9vJ+mr4BOUw==, - } - dev: false - - /lodash.flatten@4.4.0: - resolution: - { - integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==, - } - dev: false - - /lodash.includes@4.3.0: - resolution: - { - integrity: sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==, - } - requiresBuild: true - dev: false + lodash.debounce@4.0.8: {} + + lodash.includes@4.3.0: + optional: true + + lodash.isboolean@3.0.3: optional: true - /lodash.invokemap@4.6.0: - resolution: - { - integrity: sha512-CfkycNtMqgUlfjfdh2BhKO/ZXrP8ePOX5lEU/g0R3ItJcnuxWDwokMGKx1hWcfOikmyOVx6X9IwWnDGlgKl61w==, - } - dev: false - - /lodash.isboolean@3.0.3: - resolution: - { - integrity: sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==, - } - requiresBuild: true - dev: false + lodash.isinteger@4.0.4: optional: true - /lodash.isfunction@3.0.9: - resolution: - { - integrity: sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==, - } - dev: true - - /lodash.isinteger@4.0.4: - resolution: - { - integrity: sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==, - } - requiresBuild: true - dev: false + lodash.isnumber@3.0.3: optional: true - /lodash.isnumber@3.0.3: - resolution: - { - integrity: sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==, - } - requiresBuild: true - dev: false + lodash.isplainobject@4.0.6: optional: true - /lodash.isplainobject@4.0.6: - resolution: - { - integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==, - } - - /lodash.isstring@4.0.1: - resolution: - { - integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==, - } - requiresBuild: true - dev: false + lodash.isstring@4.0.1: optional: true - /lodash.kebabcase@4.1.1: - resolution: - { - integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==, - } - - /lodash.memoize@4.1.2: - resolution: - { - integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==, - } - - /lodash.merge@4.6.2: - resolution: - { - integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==, - } - dev: true - - /lodash.mergewith@4.6.2: - resolution: - { - integrity: sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==, - } - dev: true - - /lodash.once@4.1.1: - resolution: - { - integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==, - } - requiresBuild: true - dev: false + lodash.kebabcase@4.1.1: {} + + lodash.memoize@4.1.2: {} + + lodash.merge@4.6.2: {} + + lodash.mergewith@4.6.2: {} + + lodash.once@4.1.1: optional: true - /lodash.pullall@4.2.0: - resolution: - { - integrity: sha512-VhqxBKH0ZxPpLhiu68YD1KnHmbhQJQctcipvmFnqIBDYzcIHzf3Zpu0tpeOKtR4x76p9yohc506eGdOjTmyIBg==, - } - dev: false - - /lodash.snakecase@4.1.1: - resolution: - { - integrity: sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==, - } - dev: true - - /lodash.startcase@4.4.0: - resolution: - { - integrity: sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==, - } - dev: true - - /lodash.template@4.5.0: - resolution: - { - integrity: sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==, - } + lodash.template@4.5.0: dependencies: lodash._reinterpolate: 3.0.0 lodash.templatesettings: 4.2.0 - dev: false - /lodash.templatesettings@4.2.0: - resolution: - { - integrity: sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==, - } + lodash.templatesettings@4.2.0: dependencies: lodash._reinterpolate: 3.0.0 - dev: false - - /lodash.truncate@4.4.2: - resolution: - { - integrity: sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==, - } - dev: true - - /lodash.unionby@4.8.0: - resolution: - { - integrity: sha512-e60kn4GJIunNkw6v9MxRnUuLYI/Tyuanch7ozoCtk/1irJTYBj+qNTxr5B3qVflmJhwStJBv387Cb+9VOfABMg==, - } - dev: false - - /lodash.uniq@4.5.0: - resolution: - { - integrity: sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==, - } - - /lodash.uniqby@4.7.0: - resolution: - { - integrity: sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==, - } - dev: false - - /lodash.upperfirst@4.3.1: - resolution: - { - integrity: sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==, - } - dev: true - - /lodash@4.17.21: - resolution: - { - integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==, - } - - /log-update@5.0.1: - resolution: - { - integrity: sha512-5UtUDQ/6edw4ofyljDNcOVJQ4c7OjDro4h3y8e1GQL5iYElYclVHJ3zeWchylvMaKnDbDilC8irOVyexnA/Slw==, - } - engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } - dependencies: - ansi-escapes: 5.0.0 - cli-cursor: 4.0.0 - slice-ansi: 5.0.0 + + lodash.truncate@4.4.2: {} + + lodash.unionby@4.8.0: {} + + lodash.uniq@4.5.0: {} + + lodash@4.17.21: {} + + log-update@6.1.0: + dependencies: + ansi-escapes: 7.1.1 + cli-cursor: 5.0.0 + slice-ansi: 7.1.0 strip-ansi: 7.1.0 - wrap-ansi: 8.1.0 - dev: true - - /long@4.0.0: - resolution: - { - integrity: sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==, - } - dev: false - - /long@5.2.3: - resolution: - { - integrity: sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==, - } - dev: false - - /loose-envify@1.4.0: - resolution: - { - integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==, - } - hasBin: true + wrap-ansi: 9.0.0 + + long@4.0.0: + optional: true + + long@5.2.3: {} + + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 - dev: true - /lower-case@1.1.4: - resolution: - { - integrity: sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA==, - } - dev: false - - /lower-case@2.0.2: - resolution: - { - integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==, - } + lower-case@2.0.2: dependencies: tslib: 2.6.2 - dev: false - /lru-cache@4.0.2: - resolution: - { - integrity: sha512-uQw9OqphAGiZhkuPlpFGmdTU2tEuhxTourM/19qGJrxBPHAr/f8BT1a0i/lOclESnGatdJG/UCkP9kZB/Lh1iw==, - } - requiresBuild: true + lru-cache@10.4.3: {} + + lru-cache@4.0.2: dependencies: pseudomap: 1.0.2 yallist: 2.1.2 - dev: false optional: true - /lru-cache@4.1.5: - resolution: - { - integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==, - } + lru-cache@4.1.5: dependencies: pseudomap: 1.0.2 yallist: 2.1.2 - dev: false - /lru-cache@5.1.1: - resolution: - { - integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==, - } + lru-cache@5.1.1: dependencies: yallist: 3.1.1 - /lru-cache@6.0.0: - resolution: - { - integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==, - } - engines: { node: '>=10' } + lru-cache@6.0.0: dependencies: yallist: 4.0.0 - /lru-memoizer@2.2.0: - resolution: - { - integrity: sha512-QfOZ6jNkxCcM/BkIPnFsqDhtrazLRsghi9mBwFAzol5GCvj4EkFT899Za3+QwikCg5sRX8JstioBDwOxEyzaNw==, - } - requiresBuild: true + lru-memoizer@2.2.0: dependencies: lodash.clonedeep: 4.5.0 lru-cache: 4.0.2 - dev: false optional: true - /magic-string@0.30.4: - resolution: - { - integrity: sha512-Q/TKtsC5BPm0kGqgBIF9oXAs/xEf2vRKiIB4wCRQTJOQIByZ1d+NnUOotvJOvNpi5RNIgVOMC3pOuaP1ZTDlVg==, - } - engines: { node: '>=12' } + magic-string@0.30.10: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + magic-string@0.30.4: dependencies: - '@jridgewell/sourcemap-codec': 1.4.15 - dev: true + '@jridgewell/sourcemap-codec': 1.5.5 - /make-dir@1.3.0: - resolution: - { - integrity: sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==, - } - engines: { node: '>=4' } + make-dir@1.3.0: dependencies: pify: 3.0.0 - dev: false - /make-dir@2.1.0: - resolution: - { - integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==, - } - engines: { node: '>=6' } + make-dir@2.1.0: dependencies: pify: 4.0.1 semver: 5.7.2 - dev: false - /make-dir@3.1.0: - resolution: - { - integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==, - } - engines: { node: '>=8' } + make-dir@3.1.0: dependencies: semver: 6.3.1 - dev: false - /make-dir@4.0.0: - resolution: - { - integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==, - } - engines: { node: '>=10' } + make-dir@4.0.0: dependencies: - semver: 7.5.4 - dev: true + semver: 7.7.3 - /make-error@1.3.6: - resolution: - { - integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==, - } - dev: true + make-error@1.3.6: {} - /makeerror@1.0.12: - resolution: - { - integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==, - } + makeerror@1.0.12: dependencies: tmpl: 1.0.5 - dev: true - - /map-cache@0.2.2: - resolution: - { - integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==, - } - engines: { node: '>=0.10.0' } - dev: false - - /map-obj@1.0.1: - resolution: - { - integrity: sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==, - } - engines: { node: '>=0.10.0' } - dev: true - - /map-obj@4.3.0: - resolution: - { - integrity: sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==, - } - engines: { node: '>=8' } - dev: true - - /map-visit@1.0.0: - resolution: - { - integrity: sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==, - } - engines: { node: '>=0.10.0' } + + map-cache@0.2.2: {} + + map-obj@1.0.1: {} + + map-obj@4.3.0: {} + + map-visit@1.0.0: dependencies: object-visit: 1.0.1 - dev: false - - /material-design-lite@1.3.0: - resolution: - { - integrity: sha512-ao76b0bqSTKcEMt7Pui+J/S3eVF0b3GWfuKUwfe2lP5DKlLZOwBq37e0/bXEzxrw7/SuHAuYAdoCwY6mAYhrsg==, - } - engines: { node: '>=0.12.0' } - dev: false - - /mathml-tag-names@2.1.3: - resolution: - { - integrity: sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==, - } - dev: true - - /md5.js@1.3.5: - resolution: - { - integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==, - } + + markdown-table@2.0.0: + dependencies: + repeat-string: 1.6.1 + + material-design-lite@1.3.0: {} + + math-intrinsics@1.1.0: {} + + mathml-tag-names@2.1.3: {} + + md5.js@1.3.5: dependencies: hash-base: 3.1.0 inherits: 2.0.4 safe-buffer: 5.2.1 - dev: false - - /mdn-data@2.0.14: - resolution: - { - integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==, - } - dev: false - - /mdn-data@2.0.28: - resolution: - { - integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==, - } - dev: false - - /mdn-data@2.0.30: - resolution: - { - integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==, - } - - /memfs@3.5.3: - resolution: - { - integrity: sha512-UERzLsxzllchadvbPs5aolHh65ISpKpM+ccLbOJ8/vvpBKmAWf+la7dXFy7Mr0ySHbdHrFv5kGFCUHHe6GFEmw==, - } - engines: { node: '>= 4.0.0' } + + mdn-data@2.0.14: {} + + mdn-data@2.0.28: {} + + mdn-data@2.0.30: {} + + memfs@3.5.3: dependencies: fs-monkey: 1.0.5 - /memory-fs@0.4.1: - resolution: - { - integrity: sha512-cda4JKCxReDXFXRqOHPQscuIYg1PvxbE2S2GP45rnwfEK+vZaXC8C1OFvdHIbgw0DLzowXGVoxLaAmlgRy14GQ==, - } + memfs@4.9.3: + dependencies: + '@jsonjoy.com/json-pack': 1.0.4(tslib@2.6.2) + '@jsonjoy.com/util': 1.2.0(tslib@2.6.2) + tree-dump: 1.0.2(tslib@2.6.2) + tslib: 2.6.2 + + memory-fs@0.4.1: dependencies: errno: 0.1.8 readable-stream: 2.3.8 - dev: false - /memory-fs@0.5.0: - resolution: - { - integrity: sha512-jA0rdU5KoQMC0e6ppoNRtpp6vjFq6+NY7r8hywnC7V+1Xj/MtHwGIbB1QaK/dunyjWteJzmkpd7ooeWg10T7GA==, - } - engines: { node: '>=4.3.0 <5.0.0 || >=5.10' } + memory-fs@0.5.0: dependencies: errno: 0.1.8 readable-stream: 2.3.8 - /meow@10.1.5: - resolution: - { - integrity: sha512-/d+PQ4GKmGvM9Bee/DPa8z3mXs/pkvJE2KEThngVNOqtmljC6K7NMPxtc2JeZYTmpWb9k/TmxjeL18ez3h7vCw==, - } - engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + meow@10.1.5: dependencies: '@types/minimist': 1.2.3 camelcase-keys: 7.0.2 @@ -15175,56 +17110,20 @@ packages: trim-newlines: 4.1.1 type-fest: 1.4.0 yargs-parser: 20.2.9 - dev: true - /meow@8.1.2: - resolution: - { - integrity: sha512-r85E3NdZ+mpYk1C6RjPFEMSE+s1iZMuHtsHAqY0DT3jZczl0diWUZ8g6oU7h0M9cD2EL+PzaYghhCLzR0ZNn5Q==, - } - engines: { node: '>=10' } - dependencies: - '@types/minimist': 1.2.3 - camelcase-keys: 6.2.2 - decamelize-keys: 1.1.1 - hard-rejection: 2.1.0 - minimist-options: 4.1.0 - normalize-package-data: 3.0.3 - read-pkg-up: 7.0.1 - redent: 3.0.0 - trim-newlines: 3.0.1 - type-fest: 0.18.1 - yargs-parser: 20.2.9 - dev: true + meow@12.1.1: {} + + meow@13.2.0: {} - /merge-source-map@1.1.0: - resolution: - { - integrity: sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==, - } + merge-source-map@1.1.0: dependencies: source-map: 0.6.1 - dev: false - - /merge-stream@2.0.0: - resolution: - { - integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==, - } - - /merge2@1.4.1: - resolution: - { - integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==, - } - engines: { node: '>= 8' } - - /micromatch@3.1.10: - resolution: - { - integrity: sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==, - } - engines: { node: '>=0.10.0' } + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + micromatch@3.1.10: dependencies: arr-diff: 4.0.0 array-unique: 0.3.2 @@ -15241,227 +17140,108 @@ packages: to-regex: 3.0.2 transitivePeerDependencies: - supports-color - dev: false - /micromatch@4.0.5: - resolution: - { - integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==, - } - engines: { node: '>=8.6' } + micromatch@4.0.5: dependencies: braces: 3.0.2 picomatch: 2.3.1 - /miller-rabin@4.0.1: - resolution: - { - integrity: sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==, - } - hasBin: true + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + miller-rabin@4.0.1: dependencies: bn.js: 4.12.0 brorand: 1.1.0 - dev: false - - /mime-db@1.52.0: - resolution: - { - integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==, - } - engines: { node: '>= 0.6' } - - /mime-types@2.1.35: - resolution: - { - integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==, - } - engines: { node: '>= 0.6' } + + mime-db@1.52.0: {} + + mime-db@1.54.0: {} + + mime-types@2.1.35: dependencies: mime-db: 1.52.0 - /mime@1.6.0: - resolution: - { - integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==, - } - engines: { node: '>=4' } - hasBin: true - dev: false - - /mime@2.5.2: - resolution: - { - integrity: sha512-tqkh47FzKeCPD2PUiPB6pkbMzsCasjxAfC62/Wap5qrUWcb+sFasXUC5I3gYM5iBM8v/Qpn4UK0x+j0iHyFPDg==, - } - engines: { node: '>=4.0.0' } - hasBin: true - dev: false - - /mime@3.0.0: - resolution: - { - integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==, - } - engines: { node: '>=10.0.0' } - hasBin: true - requiresBuild: true - dev: false + mime@1.6.0: {} + + mime@2.5.2: {} + + mime@3.0.0: optional: true - /mimic-fn@2.1.0: - resolution: - { - integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==, - } - engines: { node: '>=6' } - - /mimic-fn@4.0.0: - resolution: - { - integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==, - } - engines: { node: '>=12' } - dev: true - - /min-indent@1.0.1: - resolution: - { - integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==, - } - engines: { node: '>=4' } - dev: true - - /minimalistic-assert@1.0.1: - resolution: - { - integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==, - } - dev: false - - /minimalistic-crypto-utils@1.0.1: - resolution: - { - integrity: sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==, - } - dev: false - - /minimatch@3.0.8: - resolution: - { - integrity: sha512-6FsRAQsxQ61mw+qP1ZzbL9Bc78x2p5OqNgNpnoAFLTrX8n5Kxph0CsnhmKKNXTWjXqU5L0pGPR7hYk+XWZr60Q==, - } + mimic-fn@2.1.0: {} + + mimic-fn@4.0.0: {} + + mimic-function@5.0.1: {} + + min-indent@1.0.1: {} + + minimalistic-assert@1.0.1: {} + + minimalistic-crypto-utils@1.0.1: {} + + minimatch@3.0.8: dependencies: brace-expansion: 1.1.11 - dev: false - /minimatch@3.1.2: - resolution: - { - integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==, - } + minimatch@3.1.2: dependencies: brace-expansion: 1.1.11 - /minimatch@5.1.6: - resolution: - { - integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==, - } - engines: { node: '>=10' } + minimatch@5.1.6: dependencies: brace-expansion: 2.0.1 - /minimatch@9.0.1: - resolution: - { - integrity: sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==, - } - engines: { node: '>=16 || 14 >=14.17' } + minimatch@5.1.9: + dependencies: + brace-expansion: 2.1.0 + optional: true + + minimatch@9.0.1: dependencies: brace-expansion: 2.0.1 - dev: true - /minimist-options@4.1.0: - resolution: - { - integrity: sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==, - } - engines: { node: '>= 6' } + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minimist-options@4.1.0: dependencies: arrify: 1.0.1 is-plain-obj: 1.1.0 kind-of: 6.0.3 - dev: true - /minimist@1.2.8: - resolution: - { - integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==, - } + minimist@1.2.8: {} - /minipass-collect@1.0.2: - resolution: - { - integrity: sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==, - } - engines: { node: '>= 8' } + minipass-collect@1.0.2: dependencies: minipass: 3.3.6 - dev: false - /minipass-flush@1.0.5: - resolution: - { - integrity: sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==, - } - engines: { node: '>= 8' } + minipass-flush@1.0.5: dependencies: minipass: 3.3.6 - dev: false - /minipass-pipeline@1.2.4: - resolution: - { - integrity: sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==, - } - engines: { node: '>=8' } + minipass-pipeline@1.2.4: dependencies: minipass: 3.3.6 - dev: false - /minipass@3.3.6: - resolution: - { - integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==, - } - engines: { node: '>=8' } + minipass@3.3.6: dependencies: yallist: 4.0.0 - /minipass@5.0.0: - resolution: - { - integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==, - } - engines: { node: '>=8' } + minipass@5.0.0: {} - /minizlib@2.1.2: - resolution: - { - integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==, - } - engines: { node: '>= 8' } + minipass@7.1.2: {} + + minizlib@2.1.2: dependencies: minipass: 3.3.6 yallist: 4.0.0 - /mississippi@3.0.0: - resolution: - { - integrity: sha512-x471SsVjUtBRtcvd4BzKE9kFC+/2TeWgKCgw0bZcw1b9l2X3QX5vCWgF+KaZaYm87Ss//rHnWryupDrgLvmSkA==, - } - engines: { node: '>=4.0.0' } + mississippi@3.0.0: dependencies: concat-stream: 1.6.2 duplexify: 3.7.1 @@ -15473,61 +17253,42 @@ packages: pumpify: 1.5.1 stream-each: 1.2.3 through2: 2.0.5 - dev: false - /mixin-deep@1.3.2: - resolution: - { - integrity: sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==, - } - engines: { node: '>=0.10.0' } + mixin-deep@1.3.2: dependencies: for-in: 1.0.2 is-extendable: 1.0.1 - dev: false - /mkdirp@0.5.6: - resolution: - { - integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==, - } - hasBin: true + mkdirp@0.5.6: dependencies: minimist: 1.2.8 - dev: false - - /mkdirp@1.0.4: - resolution: - { - integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==, - } - engines: { node: '>=10' } - hasBin: true - /mlly@1.4.2: - resolution: - { - integrity: sha512-i/Ykufi2t1EZ6NaPLdfnZk2AX8cs0d+mTzVKuPfqPKPatxLApaBoxJQ9x1/uckXtrS/U5oisPMDkNs0yQTaBRg==, - } + mkdirp@1.0.4: {} + + mlly@1.4.2: dependencies: - acorn: 8.10.0 - pathe: 1.1.1 - pkg-types: 1.0.3 - ufo: 1.3.1 - dev: true - - /moment@2.29.4: - resolution: - { - integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==, - } - dev: false - - /move-concurrently@1.0.1: - resolution: - { - integrity: sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ==, - } + acorn: 8.15.0 + pathe: 1.1.2 + pkg-types: 1.1.2 + ufo: 1.6.1 + + mlly@1.7.1: + dependencies: + acorn: 8.15.0 + pathe: 1.1.2 + pkg-types: 1.1.2 + ufo: 1.6.1 + + mlly@1.7.3: + dependencies: + acorn: 8.15.0 + pathe: 1.1.2 + pkg-types: 1.2.1 + ufo: 1.6.1 + + moment@2.30.1: {} + + move-concurrently@1.0.1: dependencies: aproba: 1.2.0 copy-concurrently: 1.0.5 @@ -15535,81 +17296,31 @@ packages: mkdirp: 0.5.6 rimraf: 2.7.1 run-queue: 1.0.3 - dev: false - - /mri@1.2.0: - resolution: - { - integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==, - } - engines: { node: '>=4' } - dev: true - - /mrmime@1.0.1: - resolution: - { - integrity: sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==, - } - engines: { node: '>=10' } - dev: false - - /ms@2.0.0: - resolution: - { - integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==, - } - - /ms@2.1.2: - resolution: - { - integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==, - } - - /ms@2.1.3: - resolution: - { - integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==, - } - - /mustache@2.3.2: - resolution: - { - integrity: sha512-KpMNwdQsYz3O/SBS1qJ/o3sqUJ5wSb8gb0pul8CO0S56b9Y2ALm8zCfsjPXsqGFfoNBkDwZuZIAjhsZI03gYVQ==, - } - engines: { npm: '>=1.4.0' } - hasBin: true - dev: false - - /mute-stream@0.0.8: - resolution: - { - integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==, - } - dev: false - - /nan@2.18.0: - resolution: - { - integrity: sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==, - } - requiresBuild: true - dev: false + + mri@1.2.0: {} + + mrmime@1.0.1: {} + + ms@2.0.0: {} + + ms@2.1.2: {} + + ms@2.1.3: {} + + mustache@2.3.2: {} + + mute-stream@0.0.8: {} + + nan@2.18.0: optional: true - /nanoid@3.3.6: - resolution: - { - integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==, - } - engines: { node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1 } - hasBin: true + nano-spawn@1.0.2: {} + + nanoid@3.3.11: {} - /nanomatch@1.2.13: - resolution: - { - integrity: sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==, - } - engines: { node: '>=0.10.0' } + nanoid@3.3.8: {} + + nanomatch@1.2.13: dependencies: arr-diff: 4.0.0 array-unique: 0.3.2 @@ -15624,127 +17335,44 @@ packages: to-regex: 3.0.2 transitivePeerDependencies: - supports-color - dev: false - - /natural-compare@1.4.0: - resolution: - { - integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==, - } - dev: true - - /negotiator@0.6.3: - resolution: - { - integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==, - } - engines: { node: '>= 0.6' } - dev: false - - /neo-async@2.6.2: - resolution: - { - integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==, - } - - /no-case@2.3.2: - resolution: - { - integrity: sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==, - } - dependencies: - lower-case: 1.1.4 - dev: false - - /no-case@3.0.4: - resolution: - { - integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==, - } + + napi-postinstall@0.3.3: {} + + natural-compare@1.4.0: {} + + negotiator@0.6.3: {} + + neo-async@2.6.2: {} + + no-case@3.0.4: dependencies: lower-case: 2.0.2 tslib: 2.6.2 - dev: false - /node-cache@4.2.1: - resolution: - { - integrity: sha512-BOb67bWg2dTyax5kdef5WfU3X8xu4wPg+zHzkvls0Q/QpYycIFRLEEIdAx9Wma43DxG6Qzn4illdZoYseKWa4A==, - } - engines: { node: '>= 0.4.6' } + node-addon-api@1.7.2: {} + + node-cache@4.2.1: dependencies: clone: 2.1.2 lodash: 4.17.21 - dev: true - /node-fetch-native@1.4.1: - resolution: - { - integrity: sha512-NsXBU0UgBxo2rQLOeWNZqS3fvflWePMECr8CoSWoSTqCqGbVVsvl9vZu1HfQicYN0g5piV9Gh8RTEvo/uP752w==, - } + node-fetch-native@1.6.7: {} - /node-fetch@2.6.7: - resolution: - { - integrity: sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==, - } - engines: { node: 4.x || >=6.0.0 } - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true + node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 - dev: false - /node-fetch@2.7.0: - resolution: - { - integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==, - } - engines: { node: 4.x || >=6.0.0 } - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - dependencies: - whatwg-url: 5.0.0 - dev: false - - /node-forge@1.3.1: - resolution: - { - integrity: sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==, - } - engines: { node: '>= 6.13.0' } - requiresBuild: true - dev: false + node-forge@1.3.1: optional: true - /node-html-parser@6.1.10: - resolution: - { - integrity: sha512-6/uWdWxjQWQ7tMcFK2wWlrflsQUzh1HsEzlIf2j5+TtzfhT2yUvg3DwZYAmjEHeR3uX74ko7exjHW69J0tOzIg==, - } + node-html-parser@6.1.13: dependencies: css-select: 5.1.0 he: 1.2.0 - dev: false - /node-int64@0.4.0: - resolution: - { - integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==, - } - dev: true + node-int64@0.4.0: {} - /node-libs-browser@2.2.1: - resolution: - { - integrity: sha512-h/zcD8H9kaDZ9ALUWwlBUDo6TKF8a7qBSCSEGfjTVIYeqsioSKaAX+BN7NgiMGp6iSIXZ3PxgCu8KS3b71YK5Q==, - } + node-libs-browser@2.2.1: dependencies: assert: 1.5.1 browserify-zlib: 0.2.0 @@ -15769,178 +17397,88 @@ packages: url: 0.11.3 util: 0.11.1 vm-browserify: 1.1.2 - dev: false - - /node-object-hash@1.4.2: - resolution: - { - integrity: sha512-UdS4swXs85fCGWWf6t6DMGgpN/vnlKeSGEQ7hJcrs7PBFoxoKLmibc3QRb7fwiYsjdL7PX8iI/TMSlZ90dgHhQ==, - } - engines: { node: '>=0.10.0' } - dev: false - - /node-releases@2.0.13: - resolution: - { - integrity: sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ==, - } - - /node-res@5.0.1: - resolution: - { - integrity: sha512-YOleO9c7MAqoHC+Ccu2vzvV1fL6Ku49gShq3PIMKWHRgrMSih3XcwL05NbLBi6oU2J471gTBfdpVVxwT6Pfhxg==, - } + + node-object-hash@1.4.2: {} + + node-releases@2.0.27: {} + + node-res@5.0.1: dependencies: destroy: 1.2.0 etag: 1.8.1 mime-types: 2.1.35 on-finished: 2.4.1 vary: 1.1.2 - dev: false - - /nopt@6.0.0: - resolution: - { - integrity: sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==, - } - engines: { node: ^12.13.0 || ^14.15.0 || >=16.0.0 } - hasBin: true + + nopt@6.0.0: dependencies: abbrev: 1.1.1 - dev: true - /normalize-package-data@2.5.0: - resolution: - { - integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==, - } + normalize-package-data@2.5.0: dependencies: hosted-git-info: 2.8.9 resolve: 1.22.6 semver: 5.7.2 validate-npm-package-license: 3.0.4 - dev: true - /normalize-package-data@3.0.3: - resolution: - { - integrity: sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==, - } - engines: { node: '>=10' } + normalize-package-data@3.0.3: dependencies: hosted-git-info: 4.1.0 is-core-module: 2.13.0 - semver: 7.5.4 + semver: 7.7.3 validate-npm-package-license: 3.0.4 - dev: true - /normalize-path@2.1.1: - resolution: - { - integrity: sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==, - } - engines: { node: '>=0.10.0' } - requiresBuild: true + normalize-path@2.1.1: dependencies: remove-trailing-separator: 1.1.0 - dev: false optional: true - /normalize-path@3.0.0: - resolution: - { - integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==, - } - engines: { node: '>=0.10.0' } - - /normalize-range@0.1.2: - resolution: - { - integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==, - } - engines: { node: '>=0.10.0' } - dev: false - - /normalize-url@1.9.1: - resolution: - { - integrity: sha512-A48My/mtCklowHBlI8Fq2jFWK4tX4lJ5E6ytFsSOq1fzpvT0SQSgKhSg7lN5c2uYFOrUAOQp6zhhJnpp1eMloQ==, - } - engines: { node: '>=4' } + normalize-path@3.0.0: {} + + normalize-range@0.1.2: {} + + normalize-url@1.9.1: dependencies: object-assign: 4.1.1 prepend-http: 1.0.4 query-string: 4.3.4 sort-keys: 1.1.2 - dev: false - - /normalize-url@6.1.0: - resolution: - { - integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==, - } - engines: { node: '>=10' } - dev: false - - /npm-run-path@4.0.1: - resolution: - { - integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==, - } - engines: { node: '>=8' } + + normalize-url@6.1.0: {} + + npm-run-path@4.0.1: dependencies: path-key: 3.1.1 - /npm-run-path@5.1.0: - resolution: - { - integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==, - } - engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + npm-run-path@5.3.0: dependencies: path-key: 4.0.0 - dev: true - /nth-check@2.1.1: - resolution: - { - integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==, - } + nth-check@2.1.1: dependencies: boolbase: 1.0.0 - /nuxt-highlightjs@1.0.3: - resolution: - { - integrity: sha512-3UEEyVYwjN+tg+gFF2fC/K4+xMiGCQlZ+3c19f3MCa5l90JtV7QXfU/2NpTq3yY3BeAgAYSwLVbP1SWOhVsXaw==, - } + nuxt-highlightjs@1.0.3: dependencies: - highlight.js: 11.8.0 - dev: false + highlight.js: 11.11.1 - /nuxt@2.17.2(babel-core@7.0.0-bridge.0)(consola@3.2.3)(prettier@3.0.3)(typescript@4.9.5)(vue@2.7.15): - resolution: - { - integrity: sha512-7m/ViWSGAY6ffTxsgaSzWnFUc7LXxoAzSkGaK7qbBTcQSC9TlHhkgM9KIZMq6j4tjLvXadvb9Ks2JUBw69RJgA==, - } - hasBin: true - requiresBuild: true + nuxt@2.18.1(babel-core@7.0.0-bridge.0(@babel/core@7.28.4))(consola@3.2.3)(ejs@3.1.10)(handlebars@4.7.8)(prettier@3.8.1)(typescript@4.9.5)(vue@2.7.16): dependencies: - '@nuxt/babel-preset-app': 2.17.2(vue@2.7.15) - '@nuxt/builder': 2.17.2(babel-core@7.0.0-bridge.0)(prettier@3.0.3)(typescript@4.9.5)(vue@2.7.15) - '@nuxt/cli': 2.17.2 + '@nuxt/babel-preset-app': 2.18.1(vue@2.7.16) + '@nuxt/builder': 2.18.1(babel-core@7.0.0-bridge.0(@babel/core@7.28.4))(ejs@3.1.10)(handlebars@4.7.8)(prettier@3.8.1)(typescript@4.9.5)(vue@2.7.16) + '@nuxt/cli': 2.18.1 '@nuxt/components': 2.2.1(consola@3.2.3) - '@nuxt/config': 2.17.2 - '@nuxt/core': 2.17.2 - '@nuxt/generator': 2.17.2 + '@nuxt/config': 2.18.1 + '@nuxt/core': 2.18.1 + '@nuxt/generator': 2.18.1 '@nuxt/loading-screen': 2.0.4 - '@nuxt/opencollective': 0.3.3 - '@nuxt/server': 2.17.2 - '@nuxt/telemetry': 1.4.1 - '@nuxt/utils': 2.17.2 - '@nuxt/vue-app': 2.17.2 - '@nuxt/vue-renderer': 2.17.2 - '@nuxt/webpack': 2.17.2(babel-core@7.0.0-bridge.0)(prettier@3.0.3)(typescript@4.9.5)(vue@2.7.15) + '@nuxt/opencollective': 0.4.0 + '@nuxt/server': 2.18.1 + '@nuxt/telemetry': 1.5.0 + '@nuxt/utils': 2.18.1 + '@nuxt/vue-app': 2.18.1 + '@nuxt/vue-renderer': 2.18.1 + '@nuxt/webpack': 2.18.1(babel-core@7.0.0-bridge.0(@babel/core@7.28.4))(ejs@3.1.10)(handlebars@4.7.8)(prettier@3.8.1)(typescript@4.9.5)(vue@2.7.16) transitivePeerDependencies: - '@vue/compiler-sfc' - arc-templates @@ -16007,229 +17545,115 @@ packages: - webpack-cli - webpack-command - whiskers - dev: false - - /nwsapi@2.2.7: - resolution: - { - integrity: sha512-ub5E4+FBPKwAZx0UwIQOjYWGHTEq5sPqHQNRN8Z9e4A7u3Tj1weLJsL59yH9vmvqEtBHaOmT6cYQKIZOxp35FQ==, - } - dev: true - - /object-assign@4.1.1: - resolution: - { - integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==, - } - engines: { node: '>=0.10.0' } - - /object-copy@0.1.0: - resolution: - { - integrity: sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==, - } - engines: { node: '>=0.10.0' } + + nwsapi@2.2.22: {} + + nypm@0.3.9: + dependencies: + citty: 0.1.6 + consola: 3.2.3 + execa: 8.0.1 + pathe: 1.1.2 + pkg-types: 1.2.1 + ufo: 1.6.1 + + object-assign@4.1.1: {} + + object-copy@0.1.0: dependencies: copy-descriptor: 0.1.1 define-property: 0.2.5 kind-of: 3.2.2 - dev: false - - /object-hash@3.0.0: - resolution: - { - integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==, - } - engines: { node: '>= 6' } - requiresBuild: true - dev: false + + object-hash@3.0.0: optional: true - /object-inspect@1.12.3: - resolution: - { - integrity: sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==, - } - - /object-keys@1.1.1: - resolution: - { - integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==, - } - engines: { node: '>= 0.4' } - - /object-visit@1.0.1: - resolution: - { - integrity: sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==, - } - engines: { node: '>=0.10.0' } + object-inspect@1.12.3: {} + + object-keys@1.1.1: {} + + object-visit@1.0.1: dependencies: isobject: 3.0.1 - dev: false - /object.assign@4.1.4: - resolution: - { - integrity: sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==, - } - engines: { node: '>= 0.4' } + object.assign@4.1.4: dependencies: call-bind: 1.0.2 define-properties: 1.2.1 has-symbols: 1.0.3 object-keys: 1.1.1 - /object.fromentries@2.0.7: - resolution: - { - integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==, - } - engines: { node: '>= 0.4' } + object.fromentries@2.0.7: dependencies: call-bind: 1.0.2 define-properties: 1.2.1 es-abstract: 1.22.2 - dev: true - /object.getownpropertydescriptors@2.1.7: - resolution: - { - integrity: sha512-PrJz0C2xJ58FNn11XV2lr4Jt5Gzl94qpy9Lu0JlfEj14z88sqbSBJCBEzdlNUCzY2gburhbrwOZ5BHCmuNUy0g==, - } - engines: { node: '>= 0.8' } + object.getownpropertydescriptors@2.1.7: dependencies: array.prototype.reduce: 1.0.6 call-bind: 1.0.2 define-properties: 1.2.1 es-abstract: 1.22.2 safe-array-concat: 1.0.1 - dev: false - /object.groupby@1.0.1: - resolution: - { - integrity: sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==, - } + object.groupby@1.0.1: dependencies: call-bind: 1.0.2 define-properties: 1.2.1 es-abstract: 1.22.2 get-intrinsic: 1.2.1 - dev: true - /object.pick@1.3.0: - resolution: - { - integrity: sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==, - } - engines: { node: '>=0.10.0' } + object.pick@1.3.0: dependencies: isobject: 3.0.1 - dev: false - /object.values@1.1.7: - resolution: - { - integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==, - } - engines: { node: '>= 0.4' } + object.values@1.1.7: dependencies: call-bind: 1.0.2 define-properties: 1.2.1 es-abstract: 1.22.2 - dev: true - - /ohash@1.1.3: - resolution: - { - integrity: sha512-zuHHiGTYTA1sYJ/wZN+t5HKZaH23i4yI1HMwbuXm24Nid7Dv0KcuRlKoNKS9UNfAVSBlnGLcuQrnOKWOZoEGaw==, - } - dev: true - - /on-finished@2.3.0: - resolution: - { - integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==, - } - engines: { node: '>= 0.8' } + + ohash@1.1.3: {} + + ohash@1.1.4: {} + + on-finished@2.3.0: dependencies: ee-first: 1.1.1 - dev: false - /on-finished@2.4.1: - resolution: - { - integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==, - } - engines: { node: '>= 0.8' } + on-finished@2.4.1: dependencies: ee-first: 1.1.1 - dev: false - - /on-headers@1.0.2: - resolution: - { - integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==, - } - engines: { node: '>= 0.8' } - dev: false - - /once@1.4.0: - resolution: - { - integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==, - } + + on-headers@1.0.2: {} + + once@1.4.0: dependencies: wrappy: 1.0.2 - /onetime@5.1.2: - resolution: - { - integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==, - } - engines: { node: '>=6' } + onetime@5.1.2: dependencies: mimic-fn: 2.1.0 - /onetime@6.0.0: - resolution: - { - integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==, - } - engines: { node: '>=12' } + onetime@6.0.0: dependencies: mimic-fn: 4.0.0 - dev: true - /opener@1.5.2: - resolution: - { - integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==, - } - hasBin: true - dev: false + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 - /optimize-css-assets-webpack-plugin@6.0.1(webpack@4.47.0): - resolution: - { - integrity: sha512-BshV2UZPfggZLdUfN3zFBbG4sl/DynUI+YCB6fRRDWaqO2OiWN8GPcp4Y0/fEV6B3k9Hzyk3czve3V/8B/SzKQ==, - } - peerDependencies: - webpack: ^4.0.0 + opener@1.5.2: {} + + optimize-css-assets-webpack-plugin@6.0.1(webpack@4.47.0): dependencies: - cssnano: 5.1.15(postcss@8.4.31) + cssnano: 5.1.15(postcss@8.5.6) last-call-webpack-plugin: 3.0.0 - postcss: 8.4.31 + postcss: 8.5.6 webpack: 4.47.0 - dev: false - /optionator@0.9.3: - resolution: - { - integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==, - } - engines: { node: '>= 0.8.0' } + optionator@0.9.3: dependencies: '@aashutoshrathi/word-wrap': 1.2.6 deep-is: 0.1.4 @@ -16237,1922 +17661,856 @@ packages: levn: 0.4.1 prelude-ls: 1.2.1 type-check: 0.4.0 - dev: true - - /os-browserify@0.3.0: - resolution: - { - integrity: sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==, - } - dev: false - - /os-tmpdir@1.0.2: - resolution: - { - integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==, - } - engines: { node: '>=0.10.0' } - dev: false - - /p-limit@2.3.0: - resolution: - { - integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==, - } - engines: { node: '>=6' } + + os-browserify@0.3.0: {} + + os-tmpdir@1.0.2: {} + + p-limit@2.3.0: dependencies: p-try: 2.2.0 - /p-limit@3.1.0: - resolution: - { - integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==, - } - engines: { node: '>=10' } + p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 - /p-locate@3.0.0: - resolution: - { - integrity: sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==, - } - engines: { node: '>=6' } + p-locate@3.0.0: dependencies: p-limit: 2.3.0 - dev: false - /p-locate@4.1.0: - resolution: - { - integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==, - } - engines: { node: '>=8' } + p-locate@4.1.0: dependencies: p-limit: 2.3.0 - /p-locate@5.0.0: - resolution: - { - integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==, - } - engines: { node: '>=10' } + p-locate@5.0.0: dependencies: p-limit: 3.1.0 - dev: true - /p-map@4.0.0: - resolution: - { - integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==, - } - engines: { node: '>=10' } + p-map@4.0.0: dependencies: aggregate-error: 3.1.0 - dev: false - - /p-try@2.2.0: - resolution: - { - integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==, - } - engines: { node: '>=6' } - - /pako@1.0.11: - resolution: - { - integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==, - } - dev: false - - /parallel-transform@1.2.0: - resolution: - { - integrity: sha512-P2vSmIu38uIlvdcU7fDkyrxj33gTUy/ABO5ZUbGowxNCopBq/OoD42bP4UmMrJoPyk4Uqf0mu3mtWBhHCZD8yg==, - } + + p-try@2.2.0: {} + + package-json-from-dist@1.0.1: {} + + pako@1.0.11: {} + + parallel-transform@1.2.0: dependencies: cyclist: 1.0.2 inherits: 2.0.4 readable-stream: 2.3.8 - dev: false - - /param-case@2.1.1: - resolution: - { - integrity: sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w==, - } - dependencies: - no-case: 2.3.2 - dev: false - /param-case@3.0.4: - resolution: - { - integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==, - } + param-case@3.0.4: dependencies: dot-case: 3.0.4 tslib: 2.6.2 - dev: false - /parent-module@1.0.1: - resolution: - { - integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==, - } - engines: { node: '>=6' } + parent-module@1.0.1: dependencies: callsites: 3.1.0 - /parse-asn1@5.1.6: - resolution: - { - integrity: sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==, - } + parse-asn1@5.1.6: dependencies: asn1.js: 5.4.1 browserify-aes: 1.2.0 evp_bytestokey: 1.0.3 pbkdf2: 3.1.2 safe-buffer: 5.2.1 - dev: false - /parse-git-config@3.0.0: - resolution: - { - integrity: sha512-wXoQGL1D+2COYWCD35/xbiKma1Z15xvZL8cI25wvxzled58V51SJM04Urt/uznS900iQor7QO04SgdfT/XlbuA==, - } - engines: { node: '>=8' } + parse-git-config@3.0.0: dependencies: git-config-path: 2.0.0 ini: 1.3.8 - dev: false - /parse-json@4.0.0: - resolution: - { - integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==, - } - engines: { node: '>=4' } + parse-json@4.0.0: dependencies: error-ex: 1.3.2 json-parse-better-errors: 1.0.2 - dev: false - /parse-json@5.2.0: - resolution: - { - integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==, - } - engines: { node: '>=8' } + parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.22.13 + '@babel/code-frame': 7.23.5 error-ex: 1.3.2 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 - /parse-path@7.0.0: - resolution: - { - integrity: sha512-Euf9GG8WT9CdqwuWJGdf3RkUcTBArppHABkO7Lm8IzRQp0e2r/kkFnmhu4TSK30Wcu5rVAZLmfPKSBBi9tWFog==, - } + parse-path@7.0.0: dependencies: protocols: 2.0.1 - dev: false - /parse-url@8.1.0: - resolution: - { - integrity: sha512-xDvOoLU5XRrcOZvnI6b8zA6n9O9ejNk/GExuz1yBuWUGn9KA97GI6HTs6u02wKara1CeVmZhH+0TZFdWScR89w==, - } + parse-url@8.1.0: dependencies: parse-path: 7.0.0 - dev: false - - /parse5@6.0.1: - resolution: - { - integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==, - } - dev: true - - /parseurl@1.3.3: - resolution: - { - integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==, - } - engines: { node: '>= 0.8' } - dev: false - - /pascal-case@3.1.2: - resolution: - { - integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==, - } + + parse5@7.3.0: + dependencies: + entities: 6.0.1 + + parseurl@1.3.3: {} + + pascal-case@3.1.2: dependencies: no-case: 3.0.4 tslib: 2.6.2 - dev: false - - /pascalcase@0.1.1: - resolution: - { - integrity: sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==, - } - engines: { node: '>=0.10.0' } - dev: false - - /path-browserify@0.0.1: - resolution: - { - integrity: sha512-BapA40NHICOS+USX9SN4tyhq+A2RrN/Ws5F0Z5aMHDp98Fl86lX8Oti8B7uN93L4Ifv4fHOEA+pQw87gmMO/lQ==, - } - dev: false - - /path-dirname@1.0.2: - resolution: - { - integrity: sha512-ALzNPpyNq9AqXMBjeymIjFDAkAFH06mHJH/cSBHAgU0s4vfpBn6b2nf8tiRLvagKD8RbTpq2FKTBg7cl9l3c7Q==, - } - requiresBuild: true - dev: false + + pascalcase@0.1.1: {} + + path-browserify@0.0.1: {} + + path-dirname@1.0.2: optional: true - /path-exists@3.0.0: - resolution: - { - integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==, - } - engines: { node: '>=4' } - - /path-exists@4.0.0: - resolution: - { - integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==, - } - engines: { node: '>=8' } - - /path-is-absolute@1.0.1: - resolution: - { - integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==, - } - engines: { node: '>=0.10.0' } - - /path-key@3.1.1: - resolution: - { - integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==, - } - engines: { node: '>=8' } - - /path-key@4.0.0: - resolution: - { - integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==, - } - engines: { node: '>=12' } - dev: true - - /path-parse@1.0.7: - resolution: - { - integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==, - } - - /path-type@4.0.0: - resolution: - { - integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==, - } - engines: { node: '>=8' } - - /pathe@1.1.1: - resolution: - { - integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==, - } - dev: true - - /pbkdf2@3.1.2: - resolution: - { - integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==, - } - engines: { node: '>=0.12' } + path-exists@3.0.0: {} + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@3.1.1: {} + + path-key@4.0.0: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + path-type@4.0.0: {} + + path-type@5.0.0: {} + + pathe@1.1.1: {} + + pathe@1.1.2: {} + + pbkdf2@3.1.2: dependencies: create-hash: 1.2.0 create-hmac: 1.1.7 ripemd160: 2.0.2 safe-buffer: 5.2.1 sha.js: 2.4.11 - dev: false - - /perfect-debounce@1.0.0: - resolution: - { - integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==, - } - dev: true - - /picocolors@0.2.1: - resolution: - { - integrity: sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==, - } - dev: false - - /picocolors@1.0.0: - resolution: - { - integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==, - } - - /picomatch@2.3.1: - resolution: - { - integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==, - } - engines: { node: '>=8.6' } - - /pidtree@0.6.0: - resolution: - { - integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==, - } - engines: { node: '>=0.10' } - hasBin: true - dev: true - - /pify@2.3.0: - resolution: - { - integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==, - } - engines: { node: '>=0.10.0' } - dev: false - - /pify@3.0.0: - resolution: - { - integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==, - } - engines: { node: '>=4' } - dev: false - - /pify@4.0.1: - resolution: - { - integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==, - } - engines: { node: '>=6' } - dev: false - - /pify@5.0.0: - resolution: - { - integrity: sha512-eW/gHNMlxdSP6dmG6uJip6FXN0EQBwm2clYYd8Wul42Cwu/DK8HEftzsapcNdYe2MfLiIwZqsDk2RDEsTE79hA==, - } - engines: { node: '>=10' } - dev: false - - /pirates@4.0.6: - resolution: - { - integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==, - } - engines: { node: '>= 6' } - dev: true - - /pkg-dir@3.0.0: - resolution: - { - integrity: sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==, - } - engines: { node: '>=6' } + + perfect-debounce@1.0.0: {} + + picocolors@0.2.1: {} + + picocolors@1.0.0: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + pidtree@0.6.0: {} + + pify@2.3.0: {} + + pify@3.0.0: {} + + pify@4.0.1: {} + + pify@5.0.0: {} + + pirates@4.0.7: {} + + pkg-dir@3.0.0: dependencies: find-up: 3.0.0 - dev: false - /pkg-dir@4.2.0: - resolution: - { - integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==, - } - engines: { node: '>=8' } + pkg-dir@4.2.0: dependencies: find-up: 4.1.0 - /pkg-types@1.0.3: - resolution: - { - integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==, - } + pkg-types@1.1.2: dependencies: - jsonc-parser: 3.2.0 - mlly: 1.4.2 - pathe: 1.1.1 - dev: true - - /pluralize@8.0.0: - resolution: - { - integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==, - } - engines: { node: '>=4' } - dev: true - - /pnp-webpack-plugin@1.7.0(typescript@4.9.5): - resolution: - { - integrity: sha512-2Rb3vm+EXble/sMXNSu6eoBx8e79gKqhNq9F5ZWW6ERNCTE/Q0wQNne5541tE5vKjfM8hpNCYL+LGc1YTfI0dg==, - } - engines: { node: '>=6' } + confbox: 0.1.7 + mlly: 1.7.1 + pathe: 1.1.2 + + pkg-types@1.2.1: + dependencies: + confbox: 0.1.8 + mlly: 1.7.3 + pathe: 1.1.2 + + pluralize@8.0.0: {} + + pngjs@5.0.0: {} + + pnp-webpack-plugin@1.7.0(typescript@4.9.5): dependencies: ts-pnp: 1.2.0(typescript@4.9.5) transitivePeerDependencies: - typescript - dev: false - - /posix-character-classes@0.1.1: - resolution: - { - integrity: sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==, - } - engines: { node: '>=0.10.0' } - dev: false - - /postcss-attribute-case-insensitive@6.0.2(postcss@8.4.31): - resolution: - { - integrity: sha512-IRuCwwAAQbgaLhxQdQcIIK0dCVXg3XDUnzgKD8iwdiYdwU4rMWRWyl/W9/0nA4ihVpq5pyALiHB2veBJ0292pw==, - } - engines: { node: ^14 || ^16 || >=18 } - peerDependencies: - postcss: ^8.4 + + posix-character-classes@0.1.1: {} + + postcss-attribute-case-insensitive@6.0.3(postcss@8.5.6): dependencies: - postcss: 8.4.31 - postcss-selector-parser: 6.0.13 - dev: false + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 - /postcss-calc@8.2.4(postcss@8.4.31): - resolution: - { - integrity: sha512-SmWMSJmB8MRnnULldx0lQIyhSNvuDl9HfrZkaqqE/WHAhToYsAvDq+yAsA/kIyINDszOp3Rh0GFoNuH5Ypsm3Q==, - } - peerDependencies: - postcss: ^8.2.2 + postcss-calc@10.0.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 - postcss-selector-parser: 6.0.13 + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 postcss-value-parser: 4.2.0 - dev: false - /postcss-calc@9.0.1(postcss@8.4.31): - resolution: - { - integrity: sha512-TipgjGyzP5QzEhsOZUaIkeO5mKeMFpebWzRogWG/ysonUlnHcq5aJe0jOjpfzUU8PeSaBQnrE8ehR0QA5vs8PQ==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.2 + postcss-calc@8.2.4(postcss@8.5.6): dependencies: - postcss: 8.4.31 - postcss-selector-parser: 6.0.13 + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 postcss-value-parser: 4.2.0 - dev: false - /postcss-clamp@4.1.0(postcss@8.4.31): - resolution: - { - integrity: sha512-ry4b1Llo/9zz+PKC+030KUnPITTJAHeOwjfAyyB60eT0AorGLdzp52s31OsPRHRf8NchkgFoG2y6fCfn1IV1Ow==, - } - engines: { node: '>=7.6.0' } - peerDependencies: - postcss: ^8.4.6 + postcss-clamp@4.1.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-color-functional-notation@6.0.2(postcss@8.4.31): - resolution: - { - integrity: sha512-FsjSmlSufuiFBsIqQ++VxFmvX7zKndZpBkHmfXr4wqhvzM92FTEkAh703iqWTl1U3faTgqioIqCbfqdWiFVwtw==, - } - engines: { node: ^14 || ^16 || >=18 } - peerDependencies: - postcss: ^8.4 + postcss-color-functional-notation@6.0.12(postcss@8.5.6): dependencies: - '@csstools/postcss-progressive-custom-properties': 3.0.2(postcss@8.4.31) - postcss: 8.4.31 - postcss-value-parser: 4.2.0 - dev: false + '@csstools/css-color-parser': 2.0.3(@csstools/css-parser-algorithms@2.7.0(@csstools/css-tokenizer@2.3.2))(@csstools/css-tokenizer@2.3.2) + '@csstools/css-parser-algorithms': 2.7.0(@csstools/css-tokenizer@2.3.2) + '@csstools/css-tokenizer': 2.3.2 + '@csstools/postcss-progressive-custom-properties': 3.2.0(postcss@8.5.6) + '@csstools/utilities': 1.0.0(postcss@8.5.6) + postcss: 8.5.6 - /postcss-color-hex-alpha@9.0.2(postcss@8.4.31): - resolution: - { - integrity: sha512-SfPjgr//VQ/DOCf80STIAsdAs7sbIbxATvVmd+Ec7JvR8onz9pjawhq3BJM3Pie40EE3TyB0P6hft16D33Nlyg==, - } - engines: { node: ^14 || ^16 || >=18 } - peerDependencies: - postcss: ^8.4 + postcss-color-hex-alpha@9.0.4(postcss@8.5.6): dependencies: - postcss: 8.4.31 + '@csstools/utilities': 1.0.0(postcss@8.5.6) + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-color-rebeccapurple@9.0.1(postcss@8.4.31): - resolution: - { - integrity: sha512-ds4cq5BjRieizVb2PnvbJ0omg9VCo2/KzluvoFZbxuGpsGJ5BQSD93CHBooinEtangCM5YqUOerGDl4xGmOb6Q==, - } - engines: { node: ^14 || ^16 || >=18 } - peerDependencies: - postcss: ^8.4 + postcss-color-rebeccapurple@9.0.3(postcss@8.5.6): dependencies: - postcss: 8.4.31 + '@csstools/utilities': 1.0.0(postcss@8.5.6) + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-colormin@5.3.1(postcss@8.4.31): - resolution: - { - integrity: sha512-UsWQG0AqTFQmpBegeLLc1+c3jIqBNB0zlDGRWR+dQ3pRKJL1oeMzyqmH3o2PIfn9MBdNrVPWhDbT769LxCTLJQ==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-colormin@5.3.1(postcss@8.5.6): dependencies: - browserslist: 4.22.1 + browserslist: 4.28.1 caniuse-api: 3.0.0 colord: 2.9.3 - postcss: 8.4.31 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-colormin@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-EuO+bAUmutWoZYgHn2T1dG1pPqHU6L4TjzPlu4t1wZGXQ/fxV16xg2EJmYi0z+6r+MGV1yvpx1BHkUaRrPa2bw==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-colormin@7.0.1(postcss@8.5.6): dependencies: - browserslist: 4.22.1 + browserslist: 4.28.1 caniuse-api: 3.0.0 colord: 2.9.3 - postcss: 8.4.31 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-convert-values@5.1.3(postcss@8.4.31): - resolution: - { - integrity: sha512-82pC1xkJZtcJEfiLw6UXnXVXScgtBrjlO5CBmuDQc+dlb88ZYheFsjTn40+zBVi3DkfF7iezO0nJUPLcJK3pvA==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-convert-values@5.1.3(postcss@8.5.6): dependencies: - browserslist: 4.22.1 - postcss: 8.4.31 + browserslist: 4.28.1 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-convert-values@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-U5D8QhVwqT++ecmy8rnTb+RL9n/B806UVaS3m60lqle4YDFcpbS3ae5bTQIh3wOGUSDHSEtMYLs/38dNG7EYFw==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-convert-values@7.0.1(postcss@8.5.6): dependencies: - browserslist: 4.22.1 - postcss: 8.4.31 + browserslist: 4.28.1 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-custom-media@10.0.2(postcss@8.4.31): - resolution: - { - integrity: sha512-zcEFNRmDm2fZvTPdI1pIW3W//UruMcLosmMiCdpQnrCsTRzWlKQPYMa1ud9auL0BmrryKK1+JjIGn19K0UjO/w==, - } - engines: { node: ^14 || ^16 || >=18 } - peerDependencies: - postcss: ^8.4 + postcss-custom-media@10.0.7(postcss@8.5.6): dependencies: - '@csstools/cascade-layer-name-parser': 1.0.5(@csstools/css-parser-algorithms@2.3.2)(@csstools/css-tokenizer@2.2.1) - '@csstools/css-parser-algorithms': 2.3.2(@csstools/css-tokenizer@2.2.1) - '@csstools/css-tokenizer': 2.2.1 - '@csstools/media-query-list-parser': 2.1.5(@csstools/css-parser-algorithms@2.3.2)(@csstools/css-tokenizer@2.2.1) - postcss: 8.4.31 - dev: false + '@csstools/cascade-layer-name-parser': 1.0.12(@csstools/css-parser-algorithms@2.7.0(@csstools/css-tokenizer@2.3.2))(@csstools/css-tokenizer@2.3.2) + '@csstools/css-parser-algorithms': 2.7.0(@csstools/css-tokenizer@2.3.2) + '@csstools/css-tokenizer': 2.3.2 + '@csstools/media-query-list-parser': 2.1.12(@csstools/css-parser-algorithms@2.7.0(@csstools/css-tokenizer@2.3.2))(@csstools/css-tokenizer@2.3.2) + postcss: 8.5.6 - /postcss-custom-properties@13.3.2(postcss@8.4.31): - resolution: - { - integrity: sha512-2Coszybpo8lpLY24vy2CYv9AasiZ39/bs8Imv0pWMq55Gl8NWzfc24OAo3zIX7rc6uUJAqESnVOMZ6V6lpMjJA==, - } - engines: { node: ^14 || ^16 || >=18 } - peerDependencies: - postcss: ^8.4 + postcss-custom-properties@13.3.11(postcss@8.5.6): dependencies: - '@csstools/cascade-layer-name-parser': 1.0.5(@csstools/css-parser-algorithms@2.3.2)(@csstools/css-tokenizer@2.2.1) - '@csstools/css-parser-algorithms': 2.3.2(@csstools/css-tokenizer@2.2.1) - '@csstools/css-tokenizer': 2.2.1 - postcss: 8.4.31 + '@csstools/cascade-layer-name-parser': 1.0.12(@csstools/css-parser-algorithms@2.7.0(@csstools/css-tokenizer@2.3.2))(@csstools/css-tokenizer@2.3.2) + '@csstools/css-parser-algorithms': 2.7.0(@csstools/css-tokenizer@2.3.2) + '@csstools/css-tokenizer': 2.3.2 + '@csstools/utilities': 1.0.0(postcss@8.5.6) + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-custom-selectors@7.1.6(postcss@8.4.31): - resolution: - { - integrity: sha512-svsjWRaxqL3vAzv71dV0/65P24/FB8TbPX+lWyyf9SZ7aZm4S4NhCn7N3Bg+Z5sZunG3FS8xQ80LrCU9hb37cw==, - } - engines: { node: ^14 || ^16 || >=18 } - peerDependencies: - postcss: ^8.4 + postcss-custom-selectors@7.1.11(postcss@8.5.6): dependencies: - '@csstools/cascade-layer-name-parser': 1.0.5(@csstools/css-parser-algorithms@2.3.2)(@csstools/css-tokenizer@2.2.1) - '@csstools/css-parser-algorithms': 2.3.2(@csstools/css-tokenizer@2.2.1) - '@csstools/css-tokenizer': 2.2.1 - postcss: 8.4.31 - postcss-selector-parser: 6.0.13 - dev: false + '@csstools/cascade-layer-name-parser': 1.0.12(@csstools/css-parser-algorithms@2.7.0(@csstools/css-tokenizer@2.3.2))(@csstools/css-tokenizer@2.3.2) + '@csstools/css-parser-algorithms': 2.7.0(@csstools/css-tokenizer@2.3.2) + '@csstools/css-tokenizer': 2.3.2 + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 - /postcss-dir-pseudo-class@8.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-Oy5BBi0dWPwij/IA+yDYj+/OBMQ9EPqAzTHeSNUYrUWdll/PRJmcbiUj0MNcsBi681I1gcSTLvMERPaXzdbvJg==, - } - engines: { node: ^14 || ^16 || >=18 } - peerDependencies: - postcss: ^8.4 + postcss-dir-pseudo-class@8.0.1(postcss@8.5.6): dependencies: - postcss: 8.4.31 - postcss-selector-parser: 6.0.13 - dev: false + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 - /postcss-discard-comments@5.1.2(postcss@8.4.31): - resolution: - { - integrity: sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-discard-comments@5.1.2(postcss@8.5.6): dependencies: - postcss: 8.4.31 - dev: false + postcss: 8.5.6 - /postcss-discard-comments@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-p2skSGqzPMZkEQvJsgnkBhCn8gI7NzRH2683EEjrIkoMiwRELx68yoUJ3q3DGSGuQ8Ug9Gsn+OuDr46yfO+eFw==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-discard-comments@7.0.1(postcss@8.5.6): dependencies: - postcss: 8.4.31 - dev: false + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 - /postcss-discard-duplicates@5.1.0(postcss@8.4.31): - resolution: - { - integrity: sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-discard-duplicates@5.1.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 - dev: false + postcss: 8.5.6 - /postcss-discard-duplicates@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-bU1SXIizMLtDW4oSsi5C/xHKbhLlhek/0/yCnoMQany9k3nPBq+Ctsv/9oMmyqbR96HYHxZcHyK2HR5P/mqoGA==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-discard-duplicates@7.0.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 - dev: false + postcss: 8.5.6 - /postcss-discard-empty@5.1.1(postcss@8.4.31): - resolution: - { - integrity: sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-discard-empty@5.1.1(postcss@8.5.6): dependencies: - postcss: 8.4.31 - dev: false + postcss: 8.5.6 - /postcss-discard-empty@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-b+h1S1VT6dNhpcg+LpyiUrdnEZfICF0my7HAKgJixJLW7BnNmpRH34+uw/etf5AhOlIhIAuXApSzzDzMI9K/gQ==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-discard-empty@7.0.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 - dev: false + postcss: 8.5.6 - /postcss-discard-overridden@5.1.0(postcss@8.4.31): - resolution: - { - integrity: sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-discard-overridden@5.1.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 - dev: false + postcss: 8.5.6 - /postcss-discard-overridden@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-4VELwssYXDFigPYAZ8vL4yX4mUepF/oCBeeIT4OXsJPYOtvJumyz9WflmJWTfDwCUcpDR+z0zvCWBXgTx35SVw==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-discard-overridden@7.0.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 - dev: false + postcss: 8.5.6 - /postcss-double-position-gradients@5.0.2(postcss@8.4.31): - resolution: - { - integrity: sha512-KTbvdOOy8z8zb0BTkEg4/1vqlRlApdvjw8/pFoehgQl0WVO+fezDGlvo0B8xRA+XccA7ohkQCULKNsiNOx70Cw==, - } - engines: { node: ^14 || ^16 || >=18 } - peerDependencies: - postcss: ^8.4 + postcss-double-position-gradients@5.0.6(postcss@8.5.6): dependencies: - '@csstools/postcss-progressive-custom-properties': 3.0.2(postcss@8.4.31) - postcss: 8.4.31 + '@csstools/postcss-progressive-custom-properties': 3.2.0(postcss@8.5.6) + '@csstools/utilities': 1.0.0(postcss@8.5.6) + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-focus-visible@9.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-zA4TbVaIaT8npZBEROhZmlc+GBKE8AELPHXE7i4TmIUEQhw/P/mSJfY9t6tBzpQ1rABeGtEOHYrW4SboQeONMQ==, - } - engines: { node: ^14 || ^16 || >=18 } - peerDependencies: - postcss: ^8.4 + postcss-focus-visible@9.0.1(postcss@8.5.6): dependencies: - postcss: 8.4.31 - postcss-selector-parser: 6.0.13 - dev: false + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 - /postcss-focus-within@8.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-E7+J9nuQzZaA37D/MUZMX1K817RZGDab8qw6pFwzAkDd/QtlWJ9/WTKmzewNiuxzeq6WWY7ATiRePVoDKp+DnA==, - } - engines: { node: ^14 || ^16 || >=18 } - peerDependencies: - postcss: ^8.4 + postcss-focus-within@8.0.1(postcss@8.5.6): dependencies: - postcss: 8.4.31 - postcss-selector-parser: 6.0.13 - dev: false + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 - /postcss-font-variant@5.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==, - } - peerDependencies: - postcss: ^8.1.0 + postcss-font-variant@5.0.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 - dev: false + postcss: 8.5.6 - /postcss-gap-properties@5.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-YjsEEL6890P7MCv6fch6Am1yq0EhQCJMXyT4LBohiu87+4/WqR7y5W3RIv53WdA901hhytgRvjlrAhibhW4qsA==, - } - engines: { node: ^14 || ^16 || >=18 } - peerDependencies: - postcss: ^8.4 + postcss-gap-properties@5.0.1(postcss@8.5.6): dependencies: - postcss: 8.4.31 - dev: false + postcss: 8.5.6 - /postcss-html@1.5.0: - resolution: - { - integrity: sha512-kCMRWJRHKicpA166kc2lAVUGxDZL324bkj/pVOb6RhjB0Z5Krl7mN0AsVkBhVIRZZirY0lyQXG38HCVaoKVNoA==, - } - engines: { node: ^12 || >=14 } + postcss-html@1.8.1: dependencies: htmlparser2: 8.0.2 - js-tokens: 8.0.2 - postcss: 8.4.31 - postcss-safe-parser: 6.0.0(postcss@8.4.31) - dev: true + js-tokens: 9.0.1 + postcss: 8.5.6 + postcss-safe-parser: 6.0.0(postcss@8.5.6) - /postcss-image-set-function@6.0.1(postcss@8.4.31): - resolution: - { - integrity: sha512-VlZncC9hhZ5tg0JllY4g6Z28BeoPO8DIkelioEEkXL0AA0IORlqYpTi2L8TUnl4YQrlwvBgxVy+mdZJw5R/cIQ==, - } - engines: { node: ^14 || ^16 || >=18 } - peerDependencies: - postcss: ^8.4 + postcss-image-set-function@6.0.3(postcss@8.5.6): dependencies: - postcss: 8.4.31 + '@csstools/utilities': 1.0.0(postcss@8.5.6) + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-import-resolver@2.0.0: - resolution: - { - integrity: sha512-y001XYgGvVwgxyxw9J1a5kqM/vtmIQGzx34g0A0Oy44MFcy/ZboZw1hu/iN3VYFjSTRzbvd7zZJJz0Kh0AGkTw==, - } + postcss-import-resolver@2.0.0: dependencies: enhanced-resolve: 4.5.0 - /postcss-import@15.1.0(postcss@8.4.31): - resolution: - { - integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==, - } - engines: { node: '>=14.0.0' } - peerDependencies: - postcss: ^8.0.0 + postcss-import@15.1.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 + postcss: 8.5.6 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.6 - dev: false - /postcss-lab-function@6.0.7(postcss@8.4.31): - resolution: - { - integrity: sha512-4d1lhDVPukHFqkMv4G5vVcK+tgY52vwb5uR1SWKOaO5389r2q8fMxBWuXSW+YtbCOEGP0/X9KERi9E9le2pJuw==, - } - engines: { node: ^14 || ^16 || >=18 } - peerDependencies: - postcss: ^8.4 + postcss-lab-function@6.0.17(postcss@8.5.6): dependencies: - '@csstools/css-color-parser': 1.4.0(@csstools/css-parser-algorithms@2.3.2)(@csstools/css-tokenizer@2.2.1) - '@csstools/css-parser-algorithms': 2.3.2(@csstools/css-tokenizer@2.2.1) - '@csstools/css-tokenizer': 2.2.1 - '@csstools/postcss-progressive-custom-properties': 3.0.2(postcss@8.4.31) - postcss: 8.4.31 - dev: false + '@csstools/css-color-parser': 2.0.3(@csstools/css-parser-algorithms@2.7.0(@csstools/css-tokenizer@2.3.2))(@csstools/css-tokenizer@2.3.2) + '@csstools/css-parser-algorithms': 2.7.0(@csstools/css-tokenizer@2.3.2) + '@csstools/css-tokenizer': 2.3.2 + '@csstools/postcss-progressive-custom-properties': 3.2.0(postcss@8.5.6) + '@csstools/utilities': 1.0.0(postcss@8.5.6) + postcss: 8.5.6 - /postcss-loader@4.3.0(postcss@8.4.31)(webpack@4.47.0): - resolution: - { - integrity: sha512-M/dSoIiNDOo8Rk0mUqoj4kpGq91gcxCfb9PoyZVdZ76/AuhxylHDYZblNE8o+EQ9AMSASeMFEKxZf5aU6wlx1Q==, - } - engines: { node: '>= 10.13.0' } - peerDependencies: - postcss: ^7.0.0 || ^8.0.1 - webpack: ^4.0.0 || ^5.0.0 + postcss-loader@4.3.0(postcss@8.5.6)(webpack@4.47.0): dependencies: cosmiconfig: 7.1.0 klona: 2.0.6 loader-utils: 2.0.4 - postcss: 8.4.31 + postcss: 8.5.6 schema-utils: 3.3.0 - semver: 7.5.4 + semver: 7.7.3 webpack: 4.47.0 - dev: false - /postcss-logical@7.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-zYf3vHkoW82f5UZTEXChTJvH49Yl9X37axTZsJGxrCG2kOUwtaAoz9E7tqYg0lsIoJLybaL8fk/2mOi81zVIUw==, - } - engines: { node: ^14 || ^16 || >=18 } - peerDependencies: - postcss: ^8.4 + postcss-logical@7.0.1(postcss@8.5.6): dependencies: - postcss: 8.4.31 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-merge-longhand@5.1.7(postcss@8.4.31): - resolution: - { - integrity: sha512-YCI9gZB+PLNskrK0BB3/2OzPnGhPkBEwmwhfYk1ilBHYVAZB7/tkTHFBAnCrvBBOmeYyMYw3DMjT55SyxMBzjQ==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-merge-longhand@5.1.7(postcss@8.5.6): dependencies: - postcss: 8.4.31 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - stylehacks: 5.1.1(postcss@8.4.31) - dev: false + stylehacks: 5.1.1(postcss@8.5.6) - /postcss-merge-longhand@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-4VSfd1lvGkLTLYcxFuISDtWUfFS4zXe0FpF149AyziftPFQIWxjvFSKhA4MIxMe4XM3yTDgQMbSNgzIVxChbIg==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-merge-longhand@7.0.2(postcss@8.5.6): dependencies: - postcss: 8.4.31 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - stylehacks: 6.0.0(postcss@8.4.31) - dev: false + stylehacks: 7.0.2(postcss@8.5.6) - /postcss-merge-rules@5.1.4(postcss@8.4.31): - resolution: - { - integrity: sha512-0R2IuYpgU93y9lhVbO/OylTtKMVcHb67zjWIfCiKR9rWL3GUk1677LAqD/BcHizukdZEjT8Ru3oHRoAYoJy44g==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-merge-rules@5.1.4(postcss@8.5.6): dependencies: - browserslist: 4.22.1 + browserslist: 4.28.1 caniuse-api: 3.0.0 - cssnano-utils: 3.1.0(postcss@8.4.31) - postcss: 8.4.31 - postcss-selector-parser: 6.0.13 - dev: false + cssnano-utils: 3.1.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 - /postcss-merge-rules@6.0.1(postcss@8.4.31): - resolution: - { - integrity: sha512-a4tlmJIQo9SCjcfiCcCMg/ZCEe0XTkl/xK0XHBs955GWg9xDX3NwP9pwZ78QUOWB8/0XCjZeJn98Dae0zg6AAw==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-merge-rules@7.0.2(postcss@8.5.6): dependencies: - browserslist: 4.22.1 + browserslist: 4.28.1 caniuse-api: 3.0.0 - cssnano-utils: 4.0.0(postcss@8.4.31) - postcss: 8.4.31 - postcss-selector-parser: 6.0.13 - dev: false + cssnano-utils: 5.0.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 - /postcss-minify-font-values@5.1.0(postcss@8.4.31): - resolution: - { - integrity: sha512-el3mYTgx13ZAPPirSVsHqFzl+BBBDrXvbySvPGFnQcTI4iNslrPaFq4muTkLZmKlGk4gyFAYUBMH30+HurREyA==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-minify-font-values@5.1.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-minify-font-values@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-zNRAVtyh5E8ndZEYXA4WS8ZYsAp798HiIQ1V2UF/C/munLp2r1UGHwf1+6JFu7hdEhJFN+W1WJQKBrtjhFgEnA==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-minify-font-values@7.0.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-minify-gradients@5.1.1(postcss@8.4.31): - resolution: - { - integrity: sha512-VGvXMTpCEo4qHTNSa9A0a3D+dxGFZCYwR6Jokk+/3oB6flu2/PnPXAh2x7x52EkY5xlIHLm+Le8tJxe/7TNhzw==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-minify-gradients@5.1.1(postcss@8.5.6): dependencies: colord: 2.9.3 - cssnano-utils: 3.1.0(postcss@8.4.31) - postcss: 8.4.31 + cssnano-utils: 3.1.0(postcss@8.5.6) + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-minify-gradients@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-wO0F6YfVAR+K1xVxF53ueZJza3L+R3E6cp0VwuXJQejnNUH0DjcAFe3JEBeTY1dLwGa0NlDWueCA1VlEfiKgAA==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-minify-gradients@7.0.0(postcss@8.5.6): dependencies: colord: 2.9.3 - cssnano-utils: 4.0.0(postcss@8.4.31) - postcss: 8.4.31 + cssnano-utils: 5.0.0(postcss@8.5.6) + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-minify-params@5.1.4(postcss@8.4.31): - resolution: - { - integrity: sha512-+mePA3MgdmVmv6g+30rn57USjOGSAyuxUmkfiWpzalZ8aiBkdPYjXWtHuwJGm1v5Ojy0Z0LaSYhHaLJQB0P8Jw==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-minify-params@5.1.4(postcss@8.5.6): dependencies: - browserslist: 4.22.1 - cssnano-utils: 3.1.0(postcss@8.4.31) - postcss: 8.4.31 + browserslist: 4.28.1 + cssnano-utils: 3.1.0(postcss@8.5.6) + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-minify-params@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-Fz/wMQDveiS0n5JPcvsMeyNXOIMrwF88n7196puSuQSWSa+/Ofc1gDOSY2xi8+A4PqB5dlYCKk/WfqKqsI+ReQ==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-minify-params@7.0.1(postcss@8.5.6): dependencies: - browserslist: 4.22.1 - cssnano-utils: 4.0.0(postcss@8.4.31) - postcss: 8.4.31 + browserslist: 4.28.1 + cssnano-utils: 5.0.0(postcss@8.5.6) + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-minify-selectors@5.2.1(postcss@8.4.31): - resolution: - { - integrity: sha512-nPJu7OjZJTsVUmPdm2TcaiohIwxP+v8ha9NehQ2ye9szv4orirRU3SDdtUmKH+10nzn0bAyOXZ0UEr7OpvLehg==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-minify-selectors@5.2.1(postcss@8.5.6): dependencies: - postcss: 8.4.31 - postcss-selector-parser: 6.0.13 - dev: false + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 - /postcss-minify-selectors@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-ec/q9JNCOC2CRDNnypipGfOhbYPuUkewGwLnbv6omue/PSASbHSU7s6uSQ0tcFRVv731oMIx8k0SP4ZX6be/0g==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-minify-selectors@7.0.2(postcss@8.5.6): dependencies: - postcss: 8.4.31 - postcss-selector-parser: 6.0.13 - dev: false + cssesc: 3.0.0 + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 - /postcss-modules-extract-imports@3.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==, - } - engines: { node: ^10 || ^12 || >= 14 } - peerDependencies: - postcss: ^8.1.0 + postcss-modules-extract-imports@3.0.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 - dev: false + postcss: 8.5.6 - /postcss-modules-local-by-default@4.0.3(postcss@8.4.31): - resolution: - { - integrity: sha512-2/u2zraspoACtrbFRnTijMiQtb4GW4BvatjaG/bCjYQo8kLTdevCUlwuBHx2sCnSyrI3x3qj4ZK1j5LQBgzmwA==, - } - engines: { node: ^10 || ^12 || >= 14 } - peerDependencies: - postcss: ^8.1.0 + postcss-modules-local-by-default@4.0.3(postcss@8.5.6): dependencies: - icss-utils: 5.1.0(postcss@8.4.31) - postcss: 8.4.31 - postcss-selector-parser: 6.0.13 + icss-utils: 5.1.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 postcss-value-parser: 4.2.0 - dev: false - - /postcss-modules-scope@3.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==, - } - engines: { node: ^10 || ^12 || >= 14 } - peerDependencies: - postcss: ^8.1.0 - dependencies: - postcss: 8.4.31 - postcss-selector-parser: 6.0.13 - dev: false - - /postcss-modules-values@4.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==, - } - engines: { node: ^10 || ^12 || >= 14 } - peerDependencies: - postcss: ^8.1.0 - dependencies: - icss-utils: 5.1.0(postcss@8.4.31) - postcss: 8.4.31 - dev: false - - /postcss-nesting@12.0.1(postcss@8.4.31): - resolution: - { - integrity: sha512-6LCqCWP9pqwXw/njMvNK0hGY44Fxc4B2EsGbn6xDcxbNRzP8GYoxT7yabVVMLrX3quqOJ9hg2jYMsnkedOf8pA==, - } - engines: { node: ^14 || ^16 || >=18 } - peerDependencies: - postcss: ^8.4 - dependencies: - '@csstools/selector-specificity': 3.0.0(postcss-selector-parser@6.0.13) - postcss: 8.4.31 - postcss-selector-parser: 6.0.13 - dev: false - /postcss-normalize-charset@5.1.0(postcss@8.4.31): - resolution: - { - integrity: sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-modules-scope@3.0.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 - dev: false + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 - /postcss-normalize-charset@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-cqundwChbu8yO/gSWkuFDmKrCZ2vJzDAocheT2JTd0sFNA4HMGoKMfbk2B+J0OmO0t5GUkiAkSM5yF2rSLUjgQ==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-modules-values@4.0.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 - dev: false + icss-utils: 5.1.0(postcss@8.5.6) + postcss: 8.5.6 - /postcss-normalize-display-values@5.1.0(postcss@8.4.31): - resolution: - { - integrity: sha512-WP4KIM4o2dazQXWmFaqMmcvsKmhdINFblgSeRgn8BJ6vxaMyaJkwAzpPpuvSIoG/rmX3M+IrRZEz2H0glrQNEA==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-nesting@12.1.5(postcss@8.5.6): dependencies: - postcss: 8.4.31 - postcss-value-parser: 4.2.0 - dev: false + '@csstools/selector-resolve-nested': 1.1.0(postcss-selector-parser@6.1.2) + '@csstools/selector-specificity': 3.1.1(postcss-selector-parser@6.1.2) + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 - /postcss-normalize-display-values@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-Qyt5kMrvy7dJRO3OjF7zkotGfuYALETZE+4lk66sziWSPzlBEt7FrUshV6VLECkI4EN8Z863O6Nci4NXQGNzYw==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-normalize-charset@5.1.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 - postcss-value-parser: 4.2.0 - dev: false + postcss: 8.5.6 - /postcss-normalize-positions@5.1.1(postcss@8.4.31): - resolution: - { - integrity: sha512-6UpCb0G4eofTCQLFVuI3EVNZzBNPiIKcA1AKVka+31fTVySphr3VUgAIULBhxZkKgwLImhzMR2Bw1ORK+37INg==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-normalize-charset@7.0.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 - postcss-value-parser: 4.2.0 - dev: false + postcss: 8.5.6 - /postcss-normalize-positions@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-mPCzhSV8+30FZyWhxi6UoVRYd3ZBJgTRly4hOkaSifo0H+pjDYcii/aVT4YE6QpOil15a5uiv6ftnY3rm0igPg==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-normalize-display-values@5.1.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-normalize-repeat-style@5.1.1(postcss@8.4.31): - resolution: - { - integrity: sha512-mFpLspGWkQtBcWIRFLmewo8aC3ImN2i/J3v8YCFUwDnPu3Xz4rLohDO26lGjwNsQxB3YF0KKRwspGzE2JEuS0g==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-normalize-display-values@7.0.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-normalize-repeat-style@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-50W5JWEBiOOAez2AKBh4kRFm2uhrT3O1Uwdxz7k24aKtbD83vqmcVG7zoIwo6xI2FZ/HDlbrCopXhLeTpQib1A==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-normalize-positions@5.1.1(postcss@8.5.6): dependencies: - postcss: 8.4.31 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-normalize-string@5.1.0(postcss@8.4.31): - resolution: - { - integrity: sha512-oYiIJOf4T9T1N4i+abeIc7Vgm/xPCGih4bZz5Nm0/ARVJ7K6xrDlLwvwqOydvyL3RHNf8qZk6vo3aatiw/go3w==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-normalize-positions@7.0.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-normalize-string@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-KWkIB7TrPOiqb8ZZz6homet2KWKJwIlysF5ICPZrXAylGe2hzX/HSf4NTX2rRPJMAtlRsj/yfkrWGavFuB+c0w==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-normalize-repeat-style@5.1.1(postcss@8.5.6): dependencies: - postcss: 8.4.31 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-normalize-timing-functions@5.1.0(postcss@8.4.31): - resolution: - { - integrity: sha512-DOEkzJ4SAXv5xkHl0Wa9cZLF3WCBhF3o1SKVxKQAa+0pYKlueTpCgvkFAHfk+Y64ezX9+nITGrDZeVGgITJXjg==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-normalize-repeat-style@7.0.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-normalize-timing-functions@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-tpIXWciXBp5CiFs8sem90IWlw76FV4oi6QEWfQwyeREVwUy39VSeSqjAT7X0Qw650yAimYW5gkl2Gd871N5SQg==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-normalize-string@5.1.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-normalize-unicode@5.1.1(postcss@8.4.31): - resolution: - { - integrity: sha512-qnCL5jzkNUmKVhZoENp1mJiGNPcsJCs1aaRmURmeJGES23Z/ajaln+EPTD+rBeNkSryI+2WTdW+lwcVdOikrpA==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-normalize-string@7.0.0(postcss@8.5.6): dependencies: - browserslist: 4.22.1 - postcss: 8.4.31 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-normalize-unicode@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-ui5crYkb5ubEUDugDc786L/Me+DXp2dLg3fVJbqyAl0VPkAeALyAijF2zOsnZyaS1HyfPuMH0DwyY18VMFVNkg==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-normalize-timing-functions@5.1.0(postcss@8.5.6): dependencies: - browserslist: 4.22.1 - postcss: 8.4.31 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-normalize-url@5.1.0(postcss@8.4.31): - resolution: - { - integrity: sha512-5upGeDO+PVthOxSmds43ZeMeZfKH+/DKgGRD7TElkkyS46JXAUhMzIKiCa7BabPeIy3AQcTkXwVVN7DbqsiCew==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-normalize-timing-functions@7.0.0(postcss@8.5.6): dependencies: - normalize-url: 6.1.0 - postcss: 8.4.31 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-normalize-url@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-98mvh2QzIPbb02YDIrYvAg4OUzGH7s1ZgHlD3fIdTHLgPLRpv1ZTKJDnSAKr4Rt21ZQFzwhGMXxpXlfrUBKFHw==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-normalize-unicode@5.1.1(postcss@8.5.6): dependencies: - postcss: 8.4.31 + browserslist: 4.28.1 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-normalize-whitespace@5.1.1(postcss@8.4.31): - resolution: - { - integrity: sha512-83ZJ4t3NUDETIHTa3uEg6asWjSBYL5EdkVB0sDncx9ERzOKBVJIUeDO9RyA9Zwtig8El1d79HBp0JEi8wvGQnA==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-normalize-unicode@7.0.1(postcss@8.5.6): dependencies: - postcss: 8.4.31 + browserslist: 4.28.1 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-normalize-whitespace@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-7cfE1AyLiK0+ZBG6FmLziJzqQCpTQY+8XjMhMAz8WSBSCsCNNUKujgIgjCAmDT3cJ+3zjTXFkoD15ZPsckArVw==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-normalize-url@5.1.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 + normalize-url: 6.1.0 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-opacity-percentage@2.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-lyDrCOtntq5Y1JZpBFzIWm2wG9kbEdujpNt4NLannF+J9c8CgFIzPa80YQfdza+Y+yFfzbYj/rfoOsYsooUWTQ==, - } - engines: { node: ^14 || ^16 || >=18 } - peerDependencies: - postcss: ^8.2 + postcss-normalize-url@7.0.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 - dev: false + postcss: 8.5.6 + postcss-value-parser: 4.2.0 - /postcss-ordered-values@5.1.3(postcss@8.4.31): - resolution: - { - integrity: sha512-9UO79VUhPwEkzbb3RNpqqghc6lcYej1aveQteWY+4POIwlqkYE21HKWaLDF6lWNuqCobEAyTovVhtI32Rbv2RQ==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-normalize-whitespace@5.1.1(postcss@8.5.6): dependencies: - cssnano-utils: 3.1.0(postcss@8.4.31) - postcss: 8.4.31 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-ordered-values@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-K36XzUDpvfG/nWkjs6d1hRBydeIxGpKS2+n+ywlKPzx1nMYDYpoGbcjhj5AwVYJK1qV2/SDoDEnHzlPD6s3nMg==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-normalize-whitespace@7.0.0(postcss@8.5.6): dependencies: - cssnano-utils: 4.0.0(postcss@8.4.31) - postcss: 8.4.31 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-overflow-shorthand@5.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-2rlxDyeSics/hC2FuMdPnWiP9WUPZ5x7FTuArXLFVpaSQ2woPSfZS4RD59HuEokbZhs/wPUQJ1E3MT6zVv94MQ==, - } - engines: { node: ^14 || ^16 || >=18 } - peerDependencies: - postcss: ^8.4 + postcss-opacity-percentage@2.0.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 - postcss-value-parser: 4.2.0 - dev: false + postcss: 8.5.6 - /postcss-page-break@3.0.4(postcss@8.4.31): - resolution: - { - integrity: sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==, - } - peerDependencies: - postcss: ^8 + postcss-ordered-values@5.1.3(postcss@8.5.6): dependencies: - postcss: 8.4.31 - dev: false + cssnano-utils: 3.1.0(postcss@8.5.6) + postcss: 8.5.6 + postcss-value-parser: 4.2.0 - /postcss-place@9.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-qLEPD9VPH5opDVemwmRaujODF9nExn24VOC3ghgVLEvfYN7VZLwJHes0q/C9YR5hI2UC3VgBE8Wkdp1TxCXhtg==, - } - engines: { node: ^14 || ^16 || >=18 } - peerDependencies: - postcss: ^8.4 + postcss-ordered-values@7.0.1(postcss@8.5.6): dependencies: - postcss: 8.4.31 + cssnano-utils: 5.0.0(postcss@8.5.6) + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-preset-env@9.3.0(postcss@8.4.31): - resolution: - { - integrity: sha512-ycw6doPrqV6QxDCtgiyGDef61bEfiSc59HGM4gOw/wxQxmKnhuEery61oOC/5ViENz/ycpRsuhTexs1kUBTvVw==, - } - engines: { node: ^14 || ^16 || >=18 } - peerDependencies: - postcss: ^8.4 + postcss-overflow-shorthand@5.0.1(postcss@8.5.6): dependencies: - '@csstools/postcss-cascade-layers': 4.0.1(postcss@8.4.31) - '@csstools/postcss-color-function': 3.0.7(postcss@8.4.31) - '@csstools/postcss-color-mix-function': 2.0.7(postcss@8.4.31) - '@csstools/postcss-exponential-functions': 1.0.1(postcss@8.4.31) - '@csstools/postcss-font-format-keywords': 3.0.0(postcss@8.4.31) - '@csstools/postcss-gamut-mapping': 1.0.0(postcss@8.4.31) - '@csstools/postcss-gradients-interpolation-method': 4.0.7(postcss@8.4.31) - '@csstools/postcss-hwb-function': 3.0.6(postcss@8.4.31) - '@csstools/postcss-ic-unit': 3.0.2(postcss@8.4.31) - '@csstools/postcss-initial': 1.0.0(postcss@8.4.31) - '@csstools/postcss-is-pseudo-class': 4.0.3(postcss@8.4.31) - '@csstools/postcss-logical-float-and-clear': 2.0.0(postcss@8.4.31) - '@csstools/postcss-logical-overflow': 1.0.0(postcss@8.4.31) - '@csstools/postcss-logical-overscroll-behavior': 1.0.0(postcss@8.4.31) - '@csstools/postcss-logical-resize': 2.0.0(postcss@8.4.31) - '@csstools/postcss-logical-viewport-units': 2.0.3(postcss@8.4.31) - '@csstools/postcss-media-minmax': 1.1.0(postcss@8.4.31) - '@csstools/postcss-media-queries-aspect-ratio-number-values': 2.0.3(postcss@8.4.31) - '@csstools/postcss-nested-calc': 3.0.0(postcss@8.4.31) - '@csstools/postcss-normalize-display-values': 3.0.1(postcss@8.4.31) - '@csstools/postcss-oklab-function': 3.0.7(postcss@8.4.31) - '@csstools/postcss-progressive-custom-properties': 3.0.2(postcss@8.4.31) - '@csstools/postcss-relative-color-syntax': 2.0.7(postcss@8.4.31) - '@csstools/postcss-scope-pseudo-class': 3.0.0(postcss@8.4.31) - '@csstools/postcss-stepped-value-functions': 3.0.2(postcss@8.4.31) - '@csstools/postcss-text-decoration-shorthand': 3.0.3(postcss@8.4.31) - '@csstools/postcss-trigonometric-functions': 3.0.2(postcss@8.4.31) - '@csstools/postcss-unset-value': 3.0.0(postcss@8.4.31) - autoprefixer: 10.4.16(postcss@8.4.31) - browserslist: 4.22.1 - css-blank-pseudo: 6.0.0(postcss@8.4.31) - css-has-pseudo: 6.0.0(postcss@8.4.31) - css-prefers-color-scheme: 9.0.0(postcss@8.4.31) - cssdb: 7.9.0 - postcss: 8.4.31 - postcss-attribute-case-insensitive: 6.0.2(postcss@8.4.31) - postcss-clamp: 4.1.0(postcss@8.4.31) - postcss-color-functional-notation: 6.0.2(postcss@8.4.31) - postcss-color-hex-alpha: 9.0.2(postcss@8.4.31) - postcss-color-rebeccapurple: 9.0.1(postcss@8.4.31) - postcss-custom-media: 10.0.2(postcss@8.4.31) - postcss-custom-properties: 13.3.2(postcss@8.4.31) - postcss-custom-selectors: 7.1.6(postcss@8.4.31) - postcss-dir-pseudo-class: 8.0.0(postcss@8.4.31) - postcss-double-position-gradients: 5.0.2(postcss@8.4.31) - postcss-focus-visible: 9.0.0(postcss@8.4.31) - postcss-focus-within: 8.0.0(postcss@8.4.31) - postcss-font-variant: 5.0.0(postcss@8.4.31) - postcss-gap-properties: 5.0.0(postcss@8.4.31) - postcss-image-set-function: 6.0.1(postcss@8.4.31) - postcss-lab-function: 6.0.7(postcss@8.4.31) - postcss-logical: 7.0.0(postcss@8.4.31) - postcss-nesting: 12.0.1(postcss@8.4.31) - postcss-opacity-percentage: 2.0.0(postcss@8.4.31) - postcss-overflow-shorthand: 5.0.0(postcss@8.4.31) - postcss-page-break: 3.0.4(postcss@8.4.31) - postcss-place: 9.0.0(postcss@8.4.31) - postcss-pseudo-class-any-link: 9.0.0(postcss@8.4.31) - postcss-replace-overflow-wrap: 4.0.0(postcss@8.4.31) - postcss-selector-not: 7.0.1(postcss@8.4.31) + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-pseudo-class-any-link@9.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-QNCYIL98VKFKY6HGDEJpF6+K/sg9bxcUYnOmNHJxZS5wsFDFaVoPeG68WAuhsqwbIBSo/b9fjEnTwY2mTSD+uA==, - } - engines: { node: ^14 || ^16 || >=18 } - peerDependencies: - postcss: ^8.4 + postcss-page-break@3.0.4(postcss@8.5.6): dependencies: - postcss: 8.4.31 - postcss-selector-parser: 6.0.13 - dev: false + postcss: 8.5.6 - /postcss-reduce-initial@5.1.2(postcss@8.4.31): - resolution: - { - integrity: sha512-dE/y2XRaqAi6OvjzD22pjTUQ8eOfc6m/natGHgKFBK9DxFmIm69YmaRVQrGgFlEfc1HePIurY0TmDeROK05rIg==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-place@9.0.1(postcss@8.5.6): dependencies: - browserslist: 4.22.1 + postcss: 8.5.6 + postcss-value-parser: 4.2.0 + + postcss-preset-env@9.5.15(postcss@8.5.6): + dependencies: + '@csstools/postcss-cascade-layers': 4.0.6(postcss@8.5.6) + '@csstools/postcss-color-function': 3.0.17(postcss@8.5.6) + '@csstools/postcss-color-mix-function': 2.0.17(postcss@8.5.6) + '@csstools/postcss-exponential-functions': 1.0.8(postcss@8.5.6) + '@csstools/postcss-font-format-keywords': 3.0.2(postcss@8.5.6) + '@csstools/postcss-gamut-mapping': 1.0.10(postcss@8.5.6) + '@csstools/postcss-gradients-interpolation-method': 4.0.18(postcss@8.5.6) + '@csstools/postcss-hwb-function': 3.0.16(postcss@8.5.6) + '@csstools/postcss-ic-unit': 3.0.6(postcss@8.5.6) + '@csstools/postcss-initial': 1.0.1(postcss@8.5.6) + '@csstools/postcss-is-pseudo-class': 4.0.8(postcss@8.5.6) + '@csstools/postcss-light-dark-function': 1.0.6(postcss@8.5.6) + '@csstools/postcss-logical-float-and-clear': 2.0.1(postcss@8.5.6) + '@csstools/postcss-logical-overflow': 1.0.1(postcss@8.5.6) + '@csstools/postcss-logical-overscroll-behavior': 1.0.1(postcss@8.5.6) + '@csstools/postcss-logical-resize': 2.0.1(postcss@8.5.6) + '@csstools/postcss-logical-viewport-units': 2.0.10(postcss@8.5.6) + '@csstools/postcss-media-minmax': 1.1.7(postcss@8.5.6) + '@csstools/postcss-media-queries-aspect-ratio-number-values': 2.0.10(postcss@8.5.6) + '@csstools/postcss-nested-calc': 3.0.2(postcss@8.5.6) + '@csstools/postcss-normalize-display-values': 3.0.2(postcss@8.5.6) + '@csstools/postcss-oklab-function': 3.0.17(postcss@8.5.6) + '@csstools/postcss-progressive-custom-properties': 3.2.0(postcss@8.5.6) + '@csstools/postcss-relative-color-syntax': 2.0.17(postcss@8.5.6) + '@csstools/postcss-scope-pseudo-class': 3.0.1(postcss@8.5.6) + '@csstools/postcss-stepped-value-functions': 3.0.9(postcss@8.5.6) + '@csstools/postcss-text-decoration-shorthand': 3.0.7(postcss@8.5.6) + '@csstools/postcss-trigonometric-functions': 3.0.9(postcss@8.5.6) + '@csstools/postcss-unset-value': 3.0.1(postcss@8.5.6) + autoprefixer: 10.4.19(postcss@8.5.6) + browserslist: 4.28.1 + css-blank-pseudo: 6.0.2(postcss@8.5.6) + css-has-pseudo: 6.0.5(postcss@8.5.6) + css-prefers-color-scheme: 9.0.1(postcss@8.5.6) + cssdb: 8.0.2 + postcss: 8.5.6 + postcss-attribute-case-insensitive: 6.0.3(postcss@8.5.6) + postcss-clamp: 4.1.0(postcss@8.5.6) + postcss-color-functional-notation: 6.0.12(postcss@8.5.6) + postcss-color-hex-alpha: 9.0.4(postcss@8.5.6) + postcss-color-rebeccapurple: 9.0.3(postcss@8.5.6) + postcss-custom-media: 10.0.7(postcss@8.5.6) + postcss-custom-properties: 13.3.11(postcss@8.5.6) + postcss-custom-selectors: 7.1.11(postcss@8.5.6) + postcss-dir-pseudo-class: 8.0.1(postcss@8.5.6) + postcss-double-position-gradients: 5.0.6(postcss@8.5.6) + postcss-focus-visible: 9.0.1(postcss@8.5.6) + postcss-focus-within: 8.0.1(postcss@8.5.6) + postcss-font-variant: 5.0.0(postcss@8.5.6) + postcss-gap-properties: 5.0.1(postcss@8.5.6) + postcss-image-set-function: 6.0.3(postcss@8.5.6) + postcss-lab-function: 6.0.17(postcss@8.5.6) + postcss-logical: 7.0.1(postcss@8.5.6) + postcss-nesting: 12.1.5(postcss@8.5.6) + postcss-opacity-percentage: 2.0.0(postcss@8.5.6) + postcss-overflow-shorthand: 5.0.1(postcss@8.5.6) + postcss-page-break: 3.0.4(postcss@8.5.6) + postcss-place: 9.0.1(postcss@8.5.6) + postcss-pseudo-class-any-link: 9.0.2(postcss@8.5.6) + postcss-replace-overflow-wrap: 4.0.0(postcss@8.5.6) + postcss-selector-not: 7.0.2(postcss@8.5.6) + + postcss-pseudo-class-any-link@9.0.2(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 + + postcss-reduce-initial@5.1.2(postcss@8.5.6): + dependencies: + browserslist: 4.28.1 caniuse-api: 3.0.0 - postcss: 8.4.31 - dev: false + postcss: 8.5.6 - /postcss-reduce-initial@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-s2UOnidpVuXu6JiiI5U+fV2jamAw5YNA9Fdi/GRK0zLDLCfXmSGqQtzpUPtfN66RtCbb9fFHoyZdQaxOB3WxVA==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-reduce-initial@7.0.1(postcss@8.5.6): dependencies: - browserslist: 4.22.1 + browserslist: 4.28.1 caniuse-api: 3.0.0 - postcss: 8.4.31 - dev: false + postcss: 8.5.6 - /postcss-reduce-transforms@5.1.0(postcss@8.4.31): - resolution: - { - integrity: sha512-2fbdbmgir5AvpW9RLtdONx1QoYG2/EtqpNQbFASDlixBbAYuTcJ0dECwlqNqH7VbaUnEnh8SrxOe2sRIn24XyQ==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-reduce-transforms@5.1.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-reduce-transforms@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-FQ9f6xM1homnuy1wLe9lP1wujzxnwt1EwiigtWwuyf8FsqqXUDUp2Ulxf9A5yjlUOTdCJO6lonYjg1mgqIIi2w==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-reduce-transforms@7.0.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - dev: false - /postcss-replace-overflow-wrap@4.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==, - } - peerDependencies: - postcss: ^8.0.3 + postcss-replace-overflow-wrap@4.0.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 - dev: false - - /postcss-resolve-nested-selector@0.1.1: - resolution: - { - integrity: sha512-HvExULSwLqHLgUy1rl3ANIqCsvMS0WHss2UOsXhXnQaZ9VCc2oBvIpXrl00IUFT5ZDITME0o6oiXeiHr2SAIfw==, - } - dev: true - - /postcss-safe-parser@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-FARHN8pwH+WiS2OPCxJI8FuRJpTVnn6ZNFiqAM2aeW2LwTHWWmWgIyKC6cUo0L8aeKiF/14MNvnpls6R2PBeMQ==, - } - engines: { node: '>=12.0' } - peerDependencies: - postcss: ^8.3.3 + postcss: 8.5.6 + + postcss-resolve-nested-selector@0.1.1: {} + + postcss-safe-parser@6.0.0(postcss@8.4.31): dependencies: postcss: 8.4.31 - dev: true - /postcss-selector-not@7.0.1(postcss@8.4.31): - resolution: - { - integrity: sha512-1zT5C27b/zeJhchN7fP0kBr16Cc61mu7Si9uWWLoA3Px/D9tIJPKchJCkUH3tPO5D0pCFmGeApAv8XpXBQJ8SQ==, - } - engines: { node: ^14 || ^16 || >=18 } - peerDependencies: - postcss: ^8.4 + postcss-safe-parser@6.0.0(postcss@8.5.6): dependencies: - postcss: 8.4.31 - postcss-selector-parser: 6.0.13 - dev: false + postcss: 8.5.6 + + postcss-selector-not@7.0.2(postcss@8.5.6): + dependencies: + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 - /postcss-selector-parser@6.0.13: - resolution: - { - integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==, - } - engines: { node: '>=4' } + postcss-selector-parser@6.0.13: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 - /postcss-svgo@5.1.0(postcss@8.4.31): - resolution: - { - integrity: sha512-D75KsH1zm5ZrHyxPakAxJWtkyXew5qwS70v56exwvw542d9CRtTo78K0WeFxZB4G7JXKKMbEZtZayTGdIky/eA==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-selector-parser@6.1.2: dependencies: - postcss: 8.4.31 + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-svgo@5.1.0(postcss@8.5.6): + dependencies: + postcss: 8.5.6 postcss-value-parser: 4.2.0 svgo: 2.8.0 - dev: false - /postcss-svgo@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-r9zvj/wGAoAIodn84dR/kFqwhINp5YsJkLoujybWG59grR/IHx+uQ2Zo+IcOwM0jskfYX3R0mo+1Kip1VSNcvw==, - } - engines: { node: ^14 || ^16 || >= 18 } - peerDependencies: - postcss: ^8.2.15 + postcss-svgo@7.0.1(postcss@8.5.6): dependencies: - postcss: 8.4.31 + postcss: 8.5.6 postcss-value-parser: 4.2.0 - svgo: 3.0.2 - dev: false + svgo: 3.3.2 - /postcss-unique-selectors@5.1.1(postcss@8.4.31): - resolution: - { - integrity: sha512-5JiODlELrz8L2HwxfPnhOWZYWDxVHWL83ufOv84NrcgipI7TaeRsatAhK4Tr2/ZiYldpK/wBvw5BD3qfaK96GA==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-unique-selectors@5.1.1(postcss@8.5.6): dependencies: - postcss: 8.4.31 - postcss-selector-parser: 6.0.13 - dev: false + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 - /postcss-unique-selectors@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-EPQzpZNxOxP7777t73RQpZE5e9TrnCrkvp7AH7a0l89JmZiPnS82y216JowHXwpBCQitfyxrof9TK3rYbi7/Yw==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + postcss-unique-selectors@7.0.1(postcss@8.5.6): dependencies: - postcss: 8.4.31 - postcss-selector-parser: 6.0.13 - dev: false + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 - /postcss-url@10.1.3(postcss@8.4.31): - resolution: - { - integrity: sha512-FUzyxfI5l2tKmXdYc6VTu3TWZsInayEKPbiyW+P6vmmIrrb4I6CGX0BFoewgYHLK+oIL5FECEK02REYRpBvUCw==, - } - engines: { node: '>=10' } - peerDependencies: - postcss: ^8.0.0 + postcss-url@10.1.3(postcss@8.5.6): dependencies: make-dir: 3.1.0 mime: 2.5.2 minimatch: 3.0.8 - postcss: 8.4.31 + postcss: 8.5.6 xxhashjs: 0.2.2 - dev: false - /postcss-value-parser@4.2.0: - resolution: - { - integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==, - } + postcss-value-parser@4.2.0: {} - /postcss@7.0.39: - resolution: - { - integrity: sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==, - } - engines: { node: '>=6.0.0' } + postcss@7.0.39: dependencies: picocolors: 0.2.1 source-map: 0.6.1 - dev: false - /postcss@8.4.31: - resolution: - { - integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==, - } - engines: { node: ^10 || ^12 || >=14 } + postcss@8.4.31: dependencies: - nanoid: 3.3.6 + nanoid: 3.3.8 picocolors: 1.0.0 source-map-js: 1.0.2 - /prelude-ls@1.2.1: - resolution: - { - integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==, - } - engines: { node: '>= 0.8.0' } - dev: true - - /prepend-http@1.0.4: - resolution: - { - integrity: sha512-PhmXi5XmoyKw1Un4E+opM2KcsJInDvKyuOumcjjw3waw86ZNjHwVUOOWLc4bCzLdcKNaWBH9e99sbWzDQsVaYg==, - } - engines: { node: '>=0.10.0' } - dev: false - - /prettier@2.8.8: - resolution: - { - integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==, - } - engines: { node: '>=10.13.0' } - hasBin: true - requiresBuild: true - dev: false + postcss@8.5.10: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prepend-http@1.0.4: {} + + prettier@2.8.8: optional: true - /prettier@3.0.3: - resolution: - { - integrity: sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==, - } - engines: { node: '>=14' } - hasBin: true + prettier@3.8.1: {} - /pretty-bytes@5.6.0: - resolution: - { - integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==, - } - engines: { node: '>=6' } - dev: false + pretty-bytes@5.6.0: {} - /pretty-error@2.1.2: - resolution: - { - integrity: sha512-EY5oDzmsX5wvuynAByrmY0P0hcp+QpnAKbJng2A2MPjVKXCxrDSUkzghVJ4ZGPIv+JC4gX8fPUWscC0RtjsWGw==, - } + pretty-error@2.1.2: dependencies: lodash: 4.17.21 renderkid: 2.0.7 - dev: false - /pretty-format@27.5.1: - resolution: - { - integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } + pretty-format@30.2.0: dependencies: - ansi-regex: 5.0.1 + '@jest/schemas': 30.0.5 ansi-styles: 5.2.0 - react-is: 17.0.2 - dev: true - - /pretty-time@1.1.0: - resolution: - { - integrity: sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==, - } - engines: { node: '>=4' } - dev: false - - /pretty@2.0.0: - resolution: - { - integrity: sha512-G9xUchgTEiNpormdYBl+Pha50gOUovT18IvAe7EYMZ1/f9W/WWMPRn+xI68yXNMUk3QXHDwo/1wV/4NejVNe1w==, - } - engines: { node: '>=0.10.0' } + react-is: 18.3.1 + + pretty-time@1.1.0: {} + + pretty@2.0.0: dependencies: condense-newlines: 0.2.1 extend-shallow: 2.0.1 js-beautify: 1.14.9 - dev: true - - /process-nextick-args@2.0.1: - resolution: - { - integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==, - } - - /process@0.11.10: - resolution: - { - integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==, - } - engines: { node: '>= 0.6.0' } - dev: false - - /promise-inflight@1.0.1(bluebird@3.7.2): - resolution: - { - integrity: sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==, - } - peerDependencies: - bluebird: '*' - peerDependenciesMeta: - bluebird: - optional: true - dependencies: - bluebird: 3.7.2 - dev: false - /prompts@2.4.2: - resolution: - { - integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==, - } - engines: { node: '>= 6' } - dependencies: - kleur: 3.0.3 - sisteransi: 1.0.5 - dev: true + process-nextick-args@2.0.1: {} + + process@0.11.10: {} + + promise-inflight@1.0.1(bluebird@3.7.2): + optionalDependencies: + bluebird: 3.7.2 - /proper-lockfile@4.1.2: - resolution: - { - integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==, - } + proper-lockfile@4.1.2: dependencies: graceful-fs: 4.2.11 retry: 0.12.0 signal-exit: 3.0.7 - dev: false - - /proto-list@1.2.4: - resolution: - { - integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==, - } - dev: true - - /proto3-json-serializer@0.1.9: - resolution: - { - integrity: sha512-A60IisqvnuI45qNRygJjrnNjX2TMdQGMY+57tR3nul3ZgO2zXkR9OGR8AXxJhkqx84g0FTnrfi3D5fWMSdANdQ==, - } - requiresBuild: true + + proto-list@1.2.4: {} + + proto3-json-serializer@0.1.9: dependencies: protobufjs: 6.11.4 - dev: false optional: true - /protobufjs@6.11.3: - resolution: - { - integrity: sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg==, - } - hasBin: true - requiresBuild: true + protobufjs@6.11.3: dependencies: '@protobufjs/aspromise': 1.1.2 '@protobufjs/base64': 1.1.2 @@ -18165,18 +18523,11 @@ packages: '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 '@types/long': 4.0.2 - '@types/node': 20.8.0 + '@types/node': 25.1.0 long: 4.0.0 - dev: false optional: true - /protobufjs@6.11.4: - resolution: - { - integrity: sha512-5kQWPaJHi1WoCpjTGszzQ32PG2F4+wRY6BmAT4Vfw56Q2FZ4YZzK20xUYQH4YkfehY1e6QSICrJquM6xXZNcrw==, - } - hasBin: true - requiresBuild: true + protobufjs@6.11.4: dependencies: '@protobufjs/aspromise': 1.1.2 '@protobufjs/base64': 1.1.2 @@ -18189,17 +18540,11 @@ packages: '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 '@types/long': 4.0.2 - '@types/node': 20.8.0 + '@types/node': 25.1.0 long: 4.0.0 - dev: false + optional: true - /protobufjs@7.2.5: - resolution: - { - integrity: sha512-gGXRSXvxQ7UiPgfw8gevrfRWcTlSbOFg+p/N+JVJEK5VhueL2miT6qTymqAmjr1Q5WbOCyJbyrk6JfWKwlFn6A==, - } - engines: { node: '>=12.0.0' } - requiresBuild: true + protobufjs@7.2.5: dependencies: '@protobufjs/aspromise': 1.1.2 '@protobufjs/base64': 1.1.2 @@ -18211,42 +18556,18 @@ packages: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.0 - '@types/node': 20.8.0 + '@types/node': 25.1.0 long: 5.2.3 - dev: false - - /protocols@2.0.1: - resolution: - { - integrity: sha512-/XJ368cyBJ7fzLMwLKv1e4vLxOju2MNAIokcr7meSaNcVbWz/CPcW22cP04mwxOErdA5mwjA8Q6w/cdAQxVn7Q==, - } - dev: false - - /prr@1.0.1: - resolution: - { - integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==, - } - - /pseudomap@1.0.2: - resolution: - { - integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==, - } - dev: false - - /psl@1.9.0: - resolution: - { - integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==, - } - dev: true - - /public-encrypt@4.0.3: - resolution: - { - integrity: sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==, - } + + protocols@2.0.1: {} + + proxy-from-env@1.1.0: {} + + prr@1.0.1: {} + + pseudomap@1.0.2: {} + + public-encrypt@4.0.3: dependencies: bn.js: 4.12.0 browserify-rsa: 4.1.0 @@ -18254,231 +18575,118 @@ packages: parse-asn1: 5.1.6 randombytes: 2.1.0 safe-buffer: 5.2.1 - dev: false - /pump@2.0.1: - resolution: - { - integrity: sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA==, - } + pump@2.0.1: dependencies: end-of-stream: 1.4.4 once: 1.4.0 - dev: false - /pump@3.0.0: - resolution: - { - integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==, - } + pump@3.0.0: dependencies: end-of-stream: 1.4.4 once: 1.4.0 - dev: false - /pumpify@1.5.1: - resolution: - { - integrity: sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ==, - } + pumpify@1.5.1: dependencies: duplexify: 3.7.1 inherits: 2.0.4 pump: 2.0.1 - dev: false - /pumpify@2.0.1: - resolution: - { - integrity: sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==, - } - requiresBuild: true + pumpify@2.0.1: dependencies: duplexify: 4.1.2 inherits: 2.0.4 pump: 3.0.0 - dev: false optional: true - /punycode@1.4.1: - resolution: - { - integrity: sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==, - } - dev: false - - /punycode@2.3.0: - resolution: - { - integrity: sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==, - } - engines: { node: '>=6' } - - /qs@6.11.2: - resolution: - { - integrity: sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==, - } - engines: { node: '>=0.6' } + punycode@1.4.1: {} + + punycode@2.3.0: {} + + punycode@2.3.1: {} + + pure-rand@7.0.1: {} + + pusher-js@8.4.0: + dependencies: + tweetnacl: 1.0.3 + + qrcode@1.5.4: + dependencies: + dijkstrajs: 1.0.3 + pngjs: 5.0.0 + yargs: 15.4.1 + + qs@6.11.2: dependencies: side-channel: 1.0.4 - dev: false - /query-string@4.3.4: - resolution: - { - integrity: sha512-O2XLNDBIg1DnTOa+2XrIwSiXEV8h2KImXUnjhhn2+UsvZ+Es2uyd5CCRTNQlDGbzUQOW3aYCBx9rVA6dzsiY7Q==, - } - engines: { node: '>=0.10.0' } + query-string@4.3.4: dependencies: object-assign: 4.1.1 strict-uri-encode: 1.1.0 - dev: false - - /querystring-es3@0.2.1: - resolution: - { - integrity: sha512-773xhDQnZBMFobEiztv8LIl70ch5MSF/jUQVlhwFyBILqq96anmoctVIYz+ZRp0qbCKATTn6ev02M3r7Ga5vqA==, - } - engines: { node: '>=0.4.x' } - dev: false - - /querystringify@2.2.0: - resolution: - { - integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==, - } - dev: true - - /queue-microtask@1.2.3: - resolution: - { - integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==, - } - - /quick-lru@4.0.1: - resolution: - { - integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==, - } - engines: { node: '>=8' } - dev: true - - /quick-lru@5.1.1: - resolution: - { - integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==, - } - engines: { node: '>=10' } - dev: true - - /randombytes@2.1.0: - resolution: - { - integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==, - } + + querystring-es3@0.2.1: {} + + queue-microtask@1.2.3: {} + + quick-lru@5.1.1: {} + + randombytes@2.1.0: dependencies: safe-buffer: 5.2.1 - /randomfill@1.0.4: - resolution: - { - integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==, - } + randomfill@1.0.4: dependencies: randombytes: 2.1.0 safe-buffer: 5.2.1 - dev: false - - /range-parser@1.2.1: - resolution: - { - integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==, - } - engines: { node: '>= 0.6' } - dev: false - - /rc9@2.1.1: - resolution: - { - integrity: sha512-lNeOl38Ws0eNxpO3+wD1I9rkHGQyj1NU1jlzv4go2CtEnEQEUfqnIvZG7W+bC/aXdJ27n5x/yUjb6RoT9tko+Q==, - } + + range-parser@1.2.1: {} + + rc9@2.1.1: dependencies: - defu: 6.1.2 - destr: 2.0.1 + defu: 6.1.4 + destr: 2.0.3 flat: 5.0.2 - /react-is@17.0.2: - resolution: - { - integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==, - } - dev: true + rc9@2.1.2: + dependencies: + defu: 6.1.4 + destr: 2.0.3 + + react-is@18.3.1: {} - /read-cache@1.0.0: - resolution: - { - integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==, - } + read-cache@1.0.0: dependencies: pify: 2.3.0 - dev: false - /read-pkg-up@7.0.1: - resolution: - { - integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==, - } - engines: { node: '>=8' } + read-pkg-up@7.0.1: dependencies: find-up: 4.1.0 read-pkg: 5.2.0 type-fest: 0.8.1 - dev: true - /read-pkg-up@8.0.0: - resolution: - { - integrity: sha512-snVCqPczksT0HS2EC+SxUndvSzn6LRCwpfSvLrIfR5BKDQQZMaI6jPRC9dYvYFDRAuFEAnkwww8kBBNE/3VvzQ==, - } - engines: { node: '>=12' } + read-pkg-up@8.0.0: dependencies: find-up: 5.0.0 read-pkg: 6.0.0 type-fest: 1.4.0 - dev: true - /read-pkg@5.2.0: - resolution: - { - integrity: sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==, - } - engines: { node: '>=8' } + read-pkg@5.2.0: dependencies: '@types/normalize-package-data': 2.4.2 normalize-package-data: 2.5.0 parse-json: 5.2.0 type-fest: 0.6.0 - dev: true - /read-pkg@6.0.0: - resolution: - { - integrity: sha512-X1Fu3dPuk/8ZLsMhEj5f4wFAF0DWoK7qhGJvgaijocXxBmSToKfbFtqbxMO7bVjNA1dmE5huAzjXj/ey86iw9Q==, - } - engines: { node: '>=12' } + read-pkg@6.0.0: dependencies: '@types/normalize-package-data': 2.4.2 normalize-package-data: 3.0.3 parse-json: 5.2.0 type-fest: 1.4.0 - dev: true - /readable-stream@2.3.8: - resolution: - { - integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==, - } + readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 inherits: 2.0.4 @@ -18488,148 +18696,60 @@ packages: string_decoder: 1.1.1 util-deprecate: 1.0.2 - /readable-stream@3.6.2: - resolution: - { - integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==, - } - engines: { node: '>= 6' } + readable-stream@3.6.2: dependencies: inherits: 2.0.4 string_decoder: 1.3.0 util-deprecate: 1.0.2 - /readdirp@2.2.1: - resolution: - { - integrity: sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==, - } - engines: { node: '>=0.10' } - requiresBuild: true + readdirp@2.2.1: dependencies: graceful-fs: 4.2.11 micromatch: 3.1.10 readable-stream: 2.3.8 transitivePeerDependencies: - supports-color - dev: false optional: true - /readdirp@3.6.0: - resolution: - { - integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==, - } - engines: { node: '>=8.10.0' } + readdirp@3.6.0: dependencies: picomatch: 2.3.1 - /redent@3.0.0: - resolution: - { - integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==, - } - engines: { node: '>=8' } - dependencies: - indent-string: 4.0.0 - strip-indent: 3.0.0 - dev: true - - /redent@4.0.0: - resolution: - { - integrity: sha512-tYkDkVVtYkSVhuQ4zBgfvciymHaeuel+zFKXShfDnFP5SyVEP7qo70Rf1jTOTCx3vGNAbnEi/xFkcfQVMIBWag==, - } - engines: { node: '>=12' } + redent@4.0.0: dependencies: indent-string: 5.0.0 strip-indent: 4.0.0 - dev: true - /regenerate-unicode-properties@10.1.1: - resolution: - { - integrity: sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==, - } - engines: { node: '>=4' } + regenerate-unicode-properties@10.1.1: dependencies: regenerate: 1.4.2 - dev: false - - /regenerate@1.4.2: - resolution: - { - integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==, - } - dev: false - - /regenerator-runtime@0.11.1: - resolution: - { - integrity: sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==, - } - dev: true - - /regenerator-runtime@0.14.0: - resolution: - { - integrity: sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==, - } - dev: false - - /regenerator-transform@0.15.2: - resolution: - { - integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==, - } - dependencies: - '@babel/runtime': 7.23.2 - dev: false - - /regex-not@1.0.2: - resolution: - { - integrity: sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==, - } - engines: { node: '>=0.10.0' } + + regenerate@1.4.2: {} + + regenerator-runtime@0.11.1: {} + + regenerator-runtime@0.14.1: {} + + regenerator-transform@0.15.2: + dependencies: + '@babel/runtime': 7.24.7 + + regex-not@1.0.2: dependencies: extend-shallow: 3.0.2 safe-regex: 1.1.0 - dev: false - /regexp-tree@0.1.27: - resolution: - { - integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==, - } - hasBin: true - dev: true + regexp-tree@0.1.27: {} - /regexp.prototype.flags@1.5.1: - resolution: - { - integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==, - } - engines: { node: '>= 0.4' } + regexp.prototype.flags@1.5.1: dependencies: call-bind: 1.0.2 define-properties: 1.2.1 set-function-name: 2.0.1 - /regexpp@3.2.0: - resolution: - { - integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==, - } - engines: { node: '>=8' } - dev: true - - /regexpu-core@5.3.2: - resolution: - { - integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==, - } - engines: { node: '>=4' } + regexpp@3.2.0: {} + + regexpu-core@5.3.2: dependencies: '@babel/regjsgen': 0.8.0 regenerate: 1.4.2 @@ -18637,533 +18757,216 @@ packages: regjsparser: 0.9.1 unicode-match-property-ecmascript: 2.0.0 unicode-match-property-value-ecmascript: 2.1.0 - dev: false - /regjsparser@0.9.1: - resolution: - { - integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==, - } - hasBin: true + regjsparser@0.9.1: dependencies: jsesc: 0.5.0 - dev: false - - /relateurl@0.2.7: - resolution: - { - integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==, - } - engines: { node: '>= 0.10' } - dev: false - - /remove-trailing-separator@1.1.0: - resolution: - { - integrity: sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==, - } - requiresBuild: true - dev: false + + relateurl@0.2.7: {} + + remove-trailing-separator@1.1.0: optional: true - /renderkid@2.0.7: - resolution: - { - integrity: sha512-oCcFyxaMrKsKcTY59qnCAtmDVSLfPbrv6A3tVbPdFMMrv5jaK10V6m40cKsoPNhAqN6rmHW9sswW4o3ruSrwUQ==, - } + renderkid@2.0.7: dependencies: css-select: 4.3.0 dom-converter: 0.2.0 htmlparser2: 6.1.0 lodash: 4.17.21 strip-ansi: 3.0.1 - dev: false - - /repeat-element@1.1.4: - resolution: - { - integrity: sha512-LFiNfRcSu7KK3evMyYOuCzv3L10TW7yC1G2/+StMjK8Y6Vqd2MG7r/Qjw4ghtuCOjFvlnms/iMmLqpvW/ES/WQ==, - } - engines: { node: '>=0.10.0' } - dev: false - - /repeat-string@1.6.1: - resolution: - { - integrity: sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==, - } - engines: { node: '>=0.10' } - dev: false - - /require-directory@2.1.1: - resolution: - { - integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==, - } - engines: { node: '>=0.10.0' } - - /require-from-string@2.0.2: - resolution: - { - integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==, - } - engines: { node: '>=0.10.0' } - - /requires-port@1.0.0: - resolution: - { - integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==, - } - dev: true - - /resolve-cwd@3.0.0: - resolution: - { - integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==, - } - engines: { node: '>=8' } + + repeat-element@1.1.4: {} + + repeat-string@1.6.1: {} + + require-directory@2.1.1: {} + + require-from-string@2.0.2: {} + + require-main-filename@2.0.0: {} + + resolve-cwd@3.0.0: dependencies: resolve-from: 5.0.0 - dev: true - - /resolve-from@4.0.0: - resolution: - { - integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==, - } - engines: { node: '>=4' } - - /resolve-from@5.0.0: - resolution: - { - integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==, - } - engines: { node: '>=8' } - dev: true - - /resolve-global@1.0.0: - resolution: - { - integrity: sha512-zFa12V4OLtT5XUX/Q4VLvTfBf+Ok0SPc1FNGM/z9ctUdiU618qwKpWnd0CHs3+RqROfyEg/DhuHbMWYqcgljEw==, - } - engines: { node: '>=8' } - dependencies: - global-dirs: 0.1.1 - dev: true - - /resolve-pkg-maps@1.0.0: - resolution: - { - integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==, - } - dev: true - - /resolve-url@0.2.1: - resolution: - { - integrity: sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==, - } - deprecated: https://github.com/lydell/resolve-url#deprecated - /resolve.exports@1.1.1: - resolution: - { - integrity: sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==, - } - engines: { node: '>=10' } - dev: true - - /resolve@1.22.6: - resolution: - { - integrity: sha512-njhxM7mV12JfufShqGy3Rz8j11RPdLy4xi15UurGJeoHLfJpVXKdh3ueuOqbYUcDZnffr6X739JBo5LzyahEsw==, - } - hasBin: true + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve-url@0.2.1: {} + + resolve@1.22.6: dependencies: is-core-module: 2.13.0 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - /restore-cursor@3.1.0: - resolution: - { - integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==, - } - engines: { node: '>=8' } + restore-cursor@3.1.0: dependencies: onetime: 5.1.2 signal-exit: 3.0.7 - dev: false - /restore-cursor@4.0.0: - resolution: - { - integrity: sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==, - } - engines: { node: ^12.20.0 || ^14.13.1 || >=16.0.0 } + restore-cursor@5.1.0: dependencies: - onetime: 5.1.2 - signal-exit: 3.0.7 - dev: true - - /ret@0.1.15: - resolution: - { - integrity: sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==, - } - engines: { node: '>=0.12' } - dev: false - - /retry-request@4.2.2: - resolution: - { - integrity: sha512-xA93uxUD/rogV7BV59agW/JHPGXeREMWiZc9jhcwY4YdZ7QOtC7qbomYg0n4wyk2lJhggjvKvhNX8wln/Aldhg==, - } - engines: { node: '>=8.10.0' } - requiresBuild: true + onetime: 7.0.0 + signal-exit: 4.1.0 + + ret@0.1.15: {} + + retry-request@4.2.2: dependencies: - debug: 4.3.4 + debug: 4.4.3 extend: 3.0.2 transitivePeerDependencies: - supports-color - dev: false optional: true - /retry@0.12.0: - resolution: - { - integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==, - } - engines: { node: '>= 4' } - dev: false - - /retry@0.13.1: - resolution: - { - integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==, - } - engines: { node: '>= 4' } - requiresBuild: true - dev: false + retry@0.12.0: {} + + retry@0.13.1: optional: true - /reusify@1.0.4: - resolution: - { - integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==, - } - engines: { iojs: '>=1.0.0', node: '>=0.10.0' } - - /rfdc@1.3.0: - resolution: - { - integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==, - } - dev: true - - /rimraf@2.7.1: - resolution: - { - integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==, - } - hasBin: true + reusify@1.0.4: {} + + rfdc@1.4.1: {} + + rimraf@2.7.1: dependencies: glob: 7.2.3 - dev: false - /rimraf@3.0.2: - resolution: - { - integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==, - } - hasBin: true + rimraf@3.0.2: dependencies: glob: 7.2.3 - /ripemd160@2.0.2: - resolution: - { - integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==, - } + ripemd160@2.0.2: dependencies: hash-base: 3.1.0 inherits: 2.0.4 - dev: false - - /rollup@2.79.1: - resolution: - { - integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==, - } - engines: { node: '>=10.0.0' } - hasBin: true + + rollup@2.79.2: optionalDependencies: fsevents: 2.3.3 - dev: true - - /rollup@3.29.4: - resolution: - { - integrity: sha512-oWzmBZwvYrU0iJHtDmhsm662rC15FRXmcjCk1xD771dFDx5jJ02ufAQQTn0etB2emNk4J9EZg/yWKpsn9BWGRw==, - } - engines: { node: '>=14.18.0', npm: '>=8.0.0' } - hasBin: true + + rollup@3.30.0: optionalDependencies: fsevents: 2.3.3 - dev: true - - /run-async@2.4.1: - resolution: - { - integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==, - } - engines: { node: '>=0.12.0' } - dev: false - - /run-parallel@1.2.0: - resolution: - { - integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==, - } + + rrweb-cssom@0.8.0: {} + + run-async@2.4.1: {} + + run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 - /run-queue@1.0.3: - resolution: - { - integrity: sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==, - } + run-queue@1.0.3: dependencies: aproba: 1.2.0 - dev: false - /rxjs@6.6.7: - resolution: - { - integrity: sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ==, - } - engines: { npm: '>=2.0.0' } + rxjs@6.6.7: dependencies: tslib: 1.14.1 - dev: false - /safe-array-concat@1.0.1: - resolution: - { - integrity: sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==, - } - engines: { node: '>=0.4' } + safe-array-concat@1.0.1: dependencies: call-bind: 1.0.2 - get-intrinsic: 1.2.1 + get-intrinsic: 1.3.0 has-symbols: 1.0.3 isarray: 2.0.5 - /safe-buffer@5.1.2: - resolution: - { - integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==, - } + safe-buffer@5.1.2: {} - /safe-buffer@5.2.1: - resolution: - { - integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==, - } + safe-buffer@5.2.1: {} - /safe-regex-test@1.0.0: - resolution: - { - integrity: sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==, - } + safe-regex-test@1.0.0: dependencies: call-bind: 1.0.2 - get-intrinsic: 1.2.1 + get-intrinsic: 1.3.0 is-regex: 1.1.4 - /safe-regex@1.1.0: - resolution: - { - integrity: sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==, - } + safe-regex@1.1.0: dependencies: ret: 0.1.15 - dev: false - /safe-regex@2.1.1: - resolution: - { - integrity: sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==, - } + safe-regex@2.1.1: dependencies: regexp-tree: 0.1.27 - dev: true - /safer-buffer@2.1.2: - resolution: - { - integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==, - } + safer-buffer@2.1.2: {} - /sass-loader@10.4.1(sass@1.32.13)(webpack@5.87.0): - resolution: - { - integrity: sha512-aX/iJZTTpNUNx/OSYzo2KsjIUQHqvWsAhhUijFjAPdZTEhstjZI9zTNvkTTwsx+uNUJqUwOw5gacxQMx4hJxGQ==, - } - engines: { node: '>= 10.13.0' } - peerDependencies: - fibers: '>= 3.1.0' - node-sass: ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 - sass: ^1.3.0 - webpack: ^4.36.0 || ^5.0.0 - peerDependenciesMeta: - fibers: - optional: true - node-sass: - optional: true - sass: - optional: true + sass-loader@10.4.1(sass@1.32.13)(webpack@5.104.1): dependencies: klona: 2.0.6 loader-utils: 2.0.4 neo-async: 2.6.2 - sass: 1.32.13 schema-utils: 3.3.0 - semver: 7.5.4 - webpack: 5.87.0 - dev: true - - /sass@1.32.13: - resolution: - { - integrity: sha512-dEgI9nShraqP7cXQH+lEXVf73WOPCse0QlFzSD8k+1TcOxCMwVXfQlr0jtoluZysQOyJGnfr21dLvYKDJq8HkA==, - } - engines: { node: '>=8.9.0' } - hasBin: true + semver: 7.7.3 + webpack: 5.104.1 + optionalDependencies: + sass: 1.32.13 + + sass@1.32.13: dependencies: - chokidar: 3.5.3 - dev: true - - /sax@1.3.0: - resolution: - { - integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==, - } - dev: false - - /saxes@5.0.1: - resolution: - { - integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==, - } - engines: { node: '>=10' } + chokidar: 3.6.0 + + sax@1.4.1: {} + + saxes@6.0.0: dependencies: xmlchars: 2.2.0 - dev: true - /schema-utils@1.0.0: - resolution: - { - integrity: sha512-i27Mic4KovM/lnGsy8whRCHhc7VicJajAjTrYg11K9zfZXnYIt4k5F+kZkwjnrhKzLic/HLU4j11mjsz2G/75g==, - } - engines: { node: '>= 4' } + schema-utils@1.0.0: dependencies: ajv: 6.12.6 ajv-errors: 1.0.1(ajv@6.12.6) ajv-keywords: 3.5.2(ajv@6.12.6) - dev: false - /schema-utils@2.7.0: - resolution: - { - integrity: sha512-0ilKFI6QQF5nxDZLFn2dMjvc4hjg/Wkg7rHd3jK6/A4a1Hl9VFdQWvgB1UMGoU94pad1P/8N7fMcEnLnSiju8A==, - } - engines: { node: '>= 8.9.0' } + schema-utils@2.7.0: dependencies: - '@types/json-schema': 7.0.13 + '@types/json-schema': 7.0.15 ajv: 6.12.6 ajv-keywords: 3.5.2(ajv@6.12.6) - dev: true - /schema-utils@2.7.1: - resolution: - { - integrity: sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==, - } - engines: { node: '>= 8.9.0' } + schema-utils@2.7.1: dependencies: - '@types/json-schema': 7.0.13 + '@types/json-schema': 7.0.15 ajv: 6.12.6 ajv-keywords: 3.5.2(ajv@6.12.6) - dev: false - /schema-utils@3.3.0: - resolution: - { - integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==, - } - engines: { node: '>= 10.13.0' } + schema-utils@3.3.0: dependencies: - '@types/json-schema': 7.0.13 + '@types/json-schema': 7.0.15 ajv: 6.12.6 ajv-keywords: 3.5.2(ajv@6.12.6) - /schema-utils@4.2.0: - resolution: - { - integrity: sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==, - } - engines: { node: '>= 12.13.0' } + schema-utils@4.3.3: dependencies: - '@types/json-schema': 7.0.13 - ajv: 8.12.0 - ajv-formats: 2.1.1(ajv@8.12.0) - ajv-keywords: 5.1.0(ajv@8.12.0) - - /scule@0.2.1: - resolution: - { - integrity: sha512-M9gnWtn3J0W+UhJOHmBxBTwv8mZCan5i1Himp60t6vvZcor0wr+IM0URKmIglsWJ7bRujNAVVN77fp+uZaWoKg==, - } - dev: false - - /scule@1.0.0: - resolution: - { - integrity: sha512-4AsO/FrViE/iDNEPaAQlb77tf0csuq27EsVpy6ett584EcRTp6pTDLoGWVxCD77y5iU5FauOvhsI4o1APwPoSQ==, - } - dev: true - - /semver@5.7.2: - resolution: - { - integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==, - } - hasBin: true + '@types/json-schema': 7.0.15 + ajv: 8.17.1 + ajv-formats: 2.1.1(ajv@8.17.1) + ajv-keywords: 5.1.0(ajv@8.17.1) - /semver@6.3.1: - resolution: - { - integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==, - } - hasBin: true + scule@0.2.1: {} - /semver@7.5.4: - resolution: - { - integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==, - } - engines: { node: '>=10' } - hasBin: true + scule@1.0.0: {} + + scule@1.3.0: {} + + semver@5.7.2: {} + + semver@6.3.1: {} + + semver@7.5.4: dependencies: lru-cache: 6.0.0 - /send@0.18.0: - resolution: - { - integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==, - } - engines: { node: '>= 0.8.0' } + semver@7.7.2: {} + + semver@7.7.3: {} + + send@0.19.0: dependencies: debug: 2.6.9 depd: 2.0.0 @@ -19180,260 +18983,127 @@ packages: statuses: 2.0.1 transitivePeerDependencies: - supports-color - dev: false - /serialize-javascript@4.0.0: - resolution: - { - integrity: sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==, - } + serialize-javascript@4.0.0: dependencies: randombytes: 2.1.0 - dev: false - /serialize-javascript@5.0.1: - resolution: - { - integrity: sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==, - } + serialize-javascript@5.0.1: dependencies: randombytes: 2.1.0 - dev: false - /serialize-javascript@6.0.1: - resolution: - { - integrity: sha512-owoXEFjWRllis8/M1Q+Cw5k8ZH40e3zhp/ovX+Xr/vi1qj6QesbyXXViFbpNvWvPNAD62SutwEXavefrLJWj7w==, - } + serialize-javascript@6.0.1: dependencies: randombytes: 2.1.0 - /serve-placeholder@2.0.1: - resolution: - { - integrity: sha512-rUzLlXk4uPFnbEaIz3SW8VISTxMuONas88nYWjAWaM2W9VDbt9tyFOr3lq8RhVOFrT3XISoBw8vni5una8qMnQ==, - } + serialize-javascript@6.0.2: dependencies: - defu: 6.1.2 - dev: false + randombytes: 2.1.0 - /serve-static@1.15.0: - resolution: - { - integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==, - } - engines: { node: '>= 0.8.0' } + serve-placeholder@2.0.2: dependencies: - encodeurl: 1.0.2 + defu: 6.1.4 + + serve-static@1.16.2: + dependencies: + encodeurl: 2.0.0 escape-html: 1.0.3 parseurl: 1.3.3 - send: 0.18.0 + send: 0.19.0 transitivePeerDependencies: - supports-color - dev: false - - /server-destroy@1.0.1: - resolution: - { - integrity: sha512-rb+9B5YBIEzYcD6x2VKidaa+cqYBJQKnU4oe4E3ANwRRN56yk/ua1YCJT1n21NTS8w6CcOclAKNP3PhdCXKYtQ==, - } - dev: false - - /set-function-name@2.0.1: - resolution: - { - integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==, - } - engines: { node: '>= 0.4' } + + server-destroy@1.0.1: {} + + set-blocking@2.0.0: {} + + set-function-name@2.0.1: dependencies: define-data-property: 1.1.0 functions-have-names: 1.2.3 has-property-descriptors: 1.0.0 - /set-value@2.0.1: - resolution: - { - integrity: sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==, - } - engines: { node: '>=0.10.0' } + set-value@2.0.1: dependencies: extend-shallow: 2.0.1 is-extendable: 0.1.1 is-plain-object: 2.0.4 split-string: 3.1.0 - dev: false - - /setimmediate@1.0.5: - resolution: - { - integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==, - } - dev: false - - /setprototypeof@1.2.0: - resolution: - { - integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==, - } - dev: false - - /sha.js@2.4.11: - resolution: - { - integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==, - } - hasBin: true + + setimmediate@1.0.5: {} + + setprototypeof@1.2.0: {} + + sha.js@2.4.11: dependencies: inherits: 2.0.4 safe-buffer: 5.2.1 - dev: false - /shebang-command@2.0.0: - resolution: - { - integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==, - } - engines: { node: '>=8' } + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 - /shebang-regex@3.0.0: - resolution: - { - integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==, - } - engines: { node: '>=8' } - - /shell-quote@1.8.1: - resolution: - { - integrity: sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==, - } - dev: false - - /side-channel@1.0.4: - resolution: - { - integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==, - } + shebang-regex@3.0.0: {} + + shell-quote@1.8.1: {} + + side-channel@1.0.4: dependencies: call-bind: 1.0.2 - get-intrinsic: 1.2.1 + get-intrinsic: 1.3.0 object-inspect: 1.12.3 - /signal-exit@3.0.7: - resolution: - { - integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==, - } - - /signal-exit@4.1.0: - resolution: - { - integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==, - } - engines: { node: '>=14' } - - /sirv@2.0.3: - resolution: - { - integrity: sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==, - } - engines: { node: '>= 10' } + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + sirv@2.0.3: dependencies: '@polka/url': 1.0.0-next.23 mrmime: 1.0.1 totalist: 3.0.1 - dev: false - - /sisteransi@1.0.5: - resolution: - { - integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==, - } - dev: true - - /sitemap@4.1.1: - resolution: - { - integrity: sha512-+8yd66IxyIFEMFkFpVoPuoPwBvdiL7Ap/HS5YD7igqO4phkyTPFIprCAE9NMHehAY5ZGN3MkAze4lDrOAX3sVQ==, - } - engines: { node: '>=8.9.0', npm: '>=5.6.0' } - hasBin: true + + sitemap@4.1.1: dependencies: '@types/node': 12.20.55 - '@types/sax': 1.2.5 + '@types/sax': 1.2.7 arg: 4.1.3 - sax: 1.3.0 + sax: 1.4.1 xmlbuilder: 13.0.2 - dev: false - - /slash@3.0.0: - resolution: - { - integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==, - } - engines: { node: '>=8' } - - /slash@4.0.0: - resolution: - { - integrity: sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==, - } - engines: { node: '>=12' } - dev: true - - /slice-ansi@4.0.0: - resolution: - { - integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==, - } - engines: { node: '>=10' } + + slash@3.0.0: {} + + slash@4.0.0: {} + + slash@5.1.0: {} + + slice-ansi@4.0.0: dependencies: ansi-styles: 4.3.0 astral-regex: 2.0.0 is-fullwidth-code-point: 3.0.0 - dev: true - /slice-ansi@5.0.0: - resolution: - { - integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==, - } - engines: { node: '>=12' } + slice-ansi@5.0.0: dependencies: - ansi-styles: 6.2.1 + ansi-styles: 6.2.3 is-fullwidth-code-point: 4.0.0 - dev: true - /snapdragon-node@2.1.1: - resolution: - { - integrity: sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==, - } - engines: { node: '>=0.10.0' } + slice-ansi@7.1.0: + dependencies: + ansi-styles: 6.2.3 + is-fullwidth-code-point: 5.0.0 + + snapdragon-node@2.1.1: dependencies: define-property: 1.0.0 isobject: 3.0.1 snapdragon-util: 3.0.1 - dev: false - /snapdragon-util@3.0.1: - resolution: - { - integrity: sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==, - } - engines: { node: '>=0.10.0' } + snapdragon-util@3.0.1: dependencies: kind-of: 3.2.2 - dev: false - /snapdragon@0.8.2: - resolution: - { - integrity: sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==, - } - engines: { node: '>=0.10.0' } + snapdragon@0.8.2: dependencies: base: 0.11.2 debug: 2.6.9 @@ -19445,48 +19115,22 @@ packages: use: 3.1.1 transitivePeerDependencies: - supports-color - dev: false - /sort-keys@1.1.2: - resolution: - { - integrity: sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg==, - } - engines: { node: '>=0.10.0' } + sort-keys@1.1.2: dependencies: is-plain-obj: 1.1.0 - dev: false - /sort-keys@2.0.0: - resolution: - { - integrity: sha512-/dPCrG1s3ePpWm6yBbxZq5Be1dXGLyLn9Z791chDC3NFrpkVbWGzkBwPN1knaciexFXgRJ7hzdnwZ4stHSDmjg==, - } - engines: { node: '>=4' } + sort-keys@2.0.0: dependencies: is-plain-obj: 1.1.0 - dev: false - - /source-list-map@2.0.1: - resolution: - { - integrity: sha512-qnQ7gVMxGNxsiL4lEuJwe/To8UnK7fAnmbGEEH8RpLouuKbeEm0lhbQVFIrNSuB+G7tVrAlVsZgETT5nljf+Iw==, - } - dev: false - - /source-map-js@1.0.2: - resolution: - { - integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==, - } - engines: { node: '>=0.10.0' } - - /source-map-resolve@0.5.3: - resolution: - { - integrity: sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==, - } - deprecated: See https://github.com/lydell/source-map-resolve#deprecated + + source-list-map@2.0.1: {} + + source-map-js@1.0.2: {} + + source-map-js@1.2.1: {} + + source-map-resolve@0.5.3: dependencies: atob: 2.1.2 decode-uri-component: 0.2.2 @@ -19494,606 +19138,265 @@ packages: source-map-url: 0.4.1 urix: 0.1.0 - /source-map-support@0.5.21: - resolution: - { - integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==, - } + source-map-support@0.5.13: dependencies: buffer-from: 1.1.2 source-map: 0.6.1 - /source-map-url@0.4.1: - resolution: - { - integrity: sha512-cPiFOTLUKvJFIg4SKVScy4ilPPW6rFgMgfuZJPNoDuMs3nC1HbMUycBoJw77xFIp6z1UJQJOfx6C9GMH80DiTw==, - } - deprecated: See https://github.com/lydell/source-map-url#deprecated + source-map-support@0.5.21: + dependencies: + buffer-from: 1.1.2 + source-map: 0.6.1 + + source-map-url@0.4.1: {} + + source-map@0.5.6: {} + + source-map@0.5.7: {} + + source-map@0.6.1: {} + + source-map@0.7.6: {} - /source-map@0.5.6: - resolution: - { - integrity: sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==, - } - engines: { node: '>=0.10.0' } - dev: false - - /source-map@0.5.7: - resolution: - { - integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==, - } - engines: { node: '>=0.10.0' } - - /source-map@0.6.1: - resolution: - { - integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==, - } - engines: { node: '>=0.10.0' } - - /source-map@0.7.4: - resolution: - { - integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==, - } - engines: { node: '>= 8' } - - /spdx-correct@3.2.0: - resolution: - { - integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==, - } + spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 spdx-license-ids: 3.0.15 - dev: true - /spdx-exceptions@2.3.0: - resolution: - { - integrity: sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==, - } - dev: true + spdx-exceptions@2.3.0: {} - /spdx-expression-parse@3.0.1: - resolution: - { - integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==, - } + spdx-expression-parse@3.0.1: dependencies: spdx-exceptions: 2.3.0 spdx-license-ids: 3.0.15 - dev: true - - /spdx-license-ids@3.0.15: - resolution: - { - integrity: sha512-lpT8hSQp9jAKp9mhtBU4Xjon8LPGBvLIuBiSVhMEtmLecTh2mO0tlqrAMp47tBXzMr13NJMQ2lf7RpQGLJ3HsQ==, - } - dev: true - - /split-string@3.1.0: - resolution: - { - integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==, - } - engines: { node: '>=0.10.0' } + + spdx-license-ids@3.0.15: {} + + split-string@3.1.0: dependencies: extend-shallow: 3.0.2 - dev: false - /split2@3.2.2: - resolution: - { - integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==, - } - dependencies: - readable-stream: 3.6.2 - dev: true + split2@4.2.0: {} - /sprintf-js@1.0.3: - resolution: - { - integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==, - } - dev: true + sprintf-js@1.0.3: {} - /ssri@6.0.2: - resolution: - { - integrity: sha512-cepbSq/neFK7xB6A50KHN0xHDotYzq58wWCa5LeWqnPrHG8GzfEjO/4O8kpmcGW+oaxkvhEJCWgbgNk4/ZV93Q==, - } + ssri@6.0.2: dependencies: figgy-pudding: 3.5.2 - dev: false - /ssri@8.0.1: - resolution: - { - integrity: sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==, - } - engines: { node: '>= 8' } + ssri@8.0.1: dependencies: minipass: 3.3.6 - dev: false - /stable@0.1.8: - resolution: - { - integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==, - } - deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' - dev: false - - /stack-trace@0.0.10: - resolution: - { - integrity: sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==, - } - dev: false - - /stack-utils@2.0.6: - resolution: - { - integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==, - } - engines: { node: '>=10' } + stable@0.1.8: {} + + stack-trace@0.0.10: {} + + stack-utils@2.0.6: dependencies: escape-string-regexp: 2.0.0 - dev: true - - /stackframe@1.3.4: - resolution: - { - integrity: sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==, - } - dev: false - - /static-extend@0.1.2: - resolution: - { - integrity: sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==, - } - engines: { node: '>=0.10.0' } + + stackframe@1.3.4: {} + + static-extend@0.1.2: dependencies: define-property: 0.2.5 object-copy: 0.1.0 - dev: false - - /statuses@1.5.0: - resolution: - { - integrity: sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==, - } - engines: { node: '>= 0.6' } - dev: false - - /statuses@2.0.1: - resolution: - { - integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==, - } - engines: { node: '>= 0.8' } - dev: false - - /std-env@3.4.3: - resolution: - { - integrity: sha512-f9aPhy8fYBuMN+sNfakZV18U39PbalgjXG3lLB9WkaYTxijru61wb57V9wxxNthXM5Sd88ETBWi29qLAsHO52Q==, - } - - /stream-browserify@2.0.2: - resolution: - { - integrity: sha512-nX6hmklHs/gr2FuxYDltq8fJA1GDlxKQCz8O/IM4atRqBH8OORmBNgfvW5gG10GT/qQ9u0CzIvr2X5Pkt6ntqg==, - } + + statuses@1.5.0: {} + + statuses@2.0.1: {} + + std-env@3.7.0: {} + + stream-browserify@2.0.2: dependencies: inherits: 2.0.4 readable-stream: 2.3.8 - dev: false - /stream-each@1.2.3: - resolution: - { - integrity: sha512-vlMC2f8I2u/bZGqkdfLQW/13Zihpej/7PmSiMQsbYddxuTsJp8vRe2x2FvVExZg7FaOds43ROAuFJwPR4MTZLw==, - } + stream-each@1.2.3: dependencies: end-of-stream: 1.4.4 stream-shift: 1.0.1 - dev: false - /stream-events@1.0.5: - resolution: - { - integrity: sha512-E1GUzBSgvct8Jsb3v2X15pjzN1tYebtbLaMg+eBOUOAxgbLoSbT2NS91ckc5lJD1KfLjId+jXJRgo0qnV5Nerg==, - } - requiresBuild: true + stream-events@1.0.5: dependencies: stubs: 3.0.0 - dev: false optional: true - /stream-http@2.8.3: - resolution: - { - integrity: sha512-+TSkfINHDo4J+ZobQLWiMouQYB+UVYFttRA94FpEzzJ7ZdqcL4uUUQ7WkdkI4DSozGmgBUE/a47L+38PenXhUw==, - } + stream-http@2.8.3: dependencies: builtin-status-codes: 3.0.0 inherits: 2.0.4 readable-stream: 2.3.8 to-arraybuffer: 1.0.1 xtend: 4.0.2 - dev: false - - /stream-shift@1.0.1: - resolution: - { - integrity: sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==, - } - dev: false - - /strict-uri-encode@1.1.0: - resolution: - { - integrity: sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==, - } - engines: { node: '>=0.10.0' } - dev: false - - /string-argv@0.3.2: - resolution: - { - integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==, - } - engines: { node: '>=0.6.19' } - dev: true - - /string-length@4.0.2: - resolution: - { - integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==, - } - engines: { node: '>=10' } + + stream-shift@1.0.1: {} + + strict-uri-encode@1.1.0: {} + + string-argv@0.3.2: {} + + string-length@4.0.2: dependencies: char-regex: 1.0.2 strip-ansi: 6.0.1 - dev: true - /string-width@4.2.3: - resolution: - { - integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==, - } - engines: { node: '>=8' } + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 - /string-width@5.1.2: - resolution: - { - integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==, - } - engines: { node: '>=12' } + string-width@5.1.2: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 - strip-ansi: 7.1.0 - dev: true + strip-ansi: 7.1.2 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.4.0 + get-east-asian-width: 1.3.0 + strip-ansi: 7.1.2 - /string.prototype.trim@1.2.8: - resolution: - { - integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==, - } - engines: { node: '>= 0.4' } + string.prototype.trim@1.2.8: dependencies: call-bind: 1.0.2 define-properties: 1.2.1 es-abstract: 1.22.2 - /string.prototype.trimend@1.0.7: - resolution: - { - integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==, - } + string.prototype.trimend@1.0.7: dependencies: call-bind: 1.0.2 define-properties: 1.2.1 es-abstract: 1.22.2 - /string.prototype.trimstart@1.0.7: - resolution: - { - integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==, - } + string.prototype.trimstart@1.0.7: dependencies: call-bind: 1.0.2 define-properties: 1.2.1 es-abstract: 1.22.2 - /string_decoder@1.1.1: - resolution: - { - integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==, - } + string_decoder@1.1.1: dependencies: safe-buffer: 5.1.2 - /string_decoder@1.3.0: - resolution: - { - integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==, - } + string_decoder@1.3.0: dependencies: safe-buffer: 5.2.1 - /strip-ansi@3.0.1: - resolution: - { - integrity: sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==, - } - engines: { node: '>=0.10.0' } + strip-ansi@3.0.1: dependencies: ansi-regex: 2.1.1 - /strip-ansi@6.0.1: - resolution: - { - integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==, - } - engines: { node: '>=8' } + strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 - /strip-ansi@7.1.0: - resolution: - { - integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==, - } - engines: { node: '>=12' } - dependencies: - ansi-regex: 6.0.1 - dev: true - - /strip-bom@3.0.0: - resolution: - { - integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==, - } - engines: { node: '>=4' } - dev: true - - /strip-bom@4.0.0: - resolution: - { - integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==, - } - engines: { node: '>=8' } - dev: true - - /strip-final-newline@2.0.0: - resolution: - { - integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==, - } - engines: { node: '>=6' } - - /strip-final-newline@3.0.0: - resolution: - { - integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==, - } - engines: { node: '>=12' } - dev: true - - /strip-indent@3.0.0: - resolution: - { - integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==, - } - engines: { node: '>=8' } + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 + + strip-ansi@7.1.2: + dependencies: + ansi-regex: 6.1.0 + + strip-bom@3.0.0: {} + + strip-bom@4.0.0: {} + + strip-final-newline@2.0.0: {} + + strip-final-newline@3.0.0: {} + + strip-indent@3.0.0: dependencies: min-indent: 1.0.1 - dev: true - /strip-indent@4.0.0: - resolution: - { - integrity: sha512-mnVSV2l+Zv6BLpSD/8V87CW/y9EmmbYzGCIavsnsI6/nwn26DwffM/yztm30Z/I2DY9wdS3vXVCMnHDgZaVNoA==, - } - engines: { node: '>=12' } + strip-indent@4.0.0: dependencies: min-indent: 1.0.1 - dev: true - - /strip-json-comments@2.0.1: - resolution: - { - integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==, - } - engines: { node: '>=0.10.0' } - dev: true - - /strip-json-comments@3.1.1: - resolution: - { - integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==, - } - engines: { node: '>=8' } - dev: true - - /strip-literal@1.3.0: - resolution: - { - integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==, - } - dependencies: - acorn: 8.10.0 - dev: true - - /stubs@3.0.0: - resolution: - { - integrity: sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==, - } - requiresBuild: true - dev: false + + strip-json-comments@2.0.1: {} + + strip-json-comments@3.1.1: {} + + strip-literal@1.3.0: + dependencies: + acorn: 8.15.0 + + strip-literal@2.1.0: + dependencies: + js-tokens: 9.0.1 + + stubs@3.0.0: optional: true - /style-resources-loader@1.5.0(webpack@4.47.0): - resolution: - { - integrity: sha512-fIfyvQ+uvXaCBGGAgfh+9v46ARQB1AWdaop2RpQw0PBVuROsTBqGvx8dj0kxwjGOAyq3vepe4AOK3M6+Q/q2jw==, - } - engines: { node: '>=8.9' } - peerDependencies: - webpack: ^3.0.0 || ^4.0.0 || ^5.0.0 + style-resources-loader@1.5.0(webpack@4.47.0): dependencies: glob: 7.2.3 loader-utils: 2.0.4 schema-utils: 2.7.1 tslib: 2.6.2 webpack: 4.47.0 - dev: false - - /style-search@0.1.0: - resolution: - { - integrity: sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==, - } - dev: true - - /stylehacks@5.1.1(postcss@8.4.31): - resolution: - { - integrity: sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==, - } - engines: { node: ^10 || ^12 || >=14.0 } - peerDependencies: - postcss: ^8.2.15 + + style-search@0.1.0: {} + + stylehacks@5.1.1(postcss@8.5.6): dependencies: - browserslist: 4.22.1 - postcss: 8.4.31 - postcss-selector-parser: 6.0.13 - dev: false + browserslist: 4.28.1 + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 - /stylehacks@6.0.0(postcss@8.4.31): - resolution: - { - integrity: sha512-+UT589qhHPwz6mTlCLSt/vMNTJx8dopeJlZAlBMJPWA3ORqu6wmQY7FBXf+qD+FsqoBJODyqNxOUP3jdntFRdw==, - } - engines: { node: ^14 || ^16 || >=18.0 } - peerDependencies: - postcss: ^8.2.15 + stylehacks@7.0.2(postcss@8.5.6): dependencies: - browserslist: 4.22.1 - postcss: 8.4.31 - postcss-selector-parser: 6.0.13 - dev: false + browserslist: 4.28.1 + postcss: 8.5.6 + postcss-selector-parser: 6.1.2 - /stylelint-config-html@1.1.0(postcss-html@1.5.0)(stylelint@15.11.0): - resolution: - { - integrity: sha512-IZv4IVESjKLumUGi+HWeb7skgO6/g4VMuAYrJdlqQFndgbj6WJAXPhaysvBiXefX79upBdQVumgYcdd17gCpjQ==, - } - engines: { node: ^12 || >=14 } - peerDependencies: - postcss-html: ^1.0.0 - stylelint: '>=14.0.0' + stylelint-config-html@1.1.0(postcss-html@1.8.1)(stylelint@15.11.0(typescript@4.9.5)): dependencies: - postcss-html: 1.5.0 + postcss-html: 1.8.1 stylelint: 15.11.0(typescript@4.9.5) - dev: true - - /stylelint-config-prettier@9.0.5(stylelint@15.11.0): - resolution: - { - integrity: sha512-U44lELgLZhbAD/xy/vncZ2Pq8sh2TnpiPvo38Ifg9+zeioR+LAkHu0i6YORIOxFafZoVg0xqQwex6e6F25S5XA==, - } - engines: { node: '>= 12' } - hasBin: true - peerDependencies: - stylelint: '>= 11.x < 15' + + stylelint-config-prettier@9.0.5(stylelint@15.11.0(typescript@4.9.5)): dependencies: stylelint: 15.11.0(typescript@4.9.5) - dev: true - /stylelint-config-recommended-vue@1.5.0(postcss-html@1.5.0)(stylelint@15.11.0): - resolution: - { - integrity: sha512-65TAK/clUqkNtkZLcuytoxU0URQYlml+30Nhop7sRkCZ/mtWdXt7T+spPSB3KMKlb+82aEVJ4OrcstyDBdbosg==, - } - engines: { node: ^12 || >=14 } - peerDependencies: - postcss-html: ^1.0.0 - stylelint: '>=14.0.0' + stylelint-config-recommended-vue@1.5.0(postcss-html@1.8.1)(stylelint@15.11.0(typescript@4.9.5)): dependencies: - postcss-html: 1.5.0 + postcss-html: 1.8.1 semver: 7.5.4 stylelint: 15.11.0(typescript@4.9.5) - stylelint-config-html: 1.1.0(postcss-html@1.5.0)(stylelint@15.11.0) - stylelint-config-recommended: 13.0.0(stylelint@15.11.0) - dev: true + stylelint-config-html: 1.1.0(postcss-html@1.8.1)(stylelint@15.11.0(typescript@4.9.5)) + stylelint-config-recommended: 13.0.0(stylelint@15.11.0(typescript@4.9.5)) - /stylelint-config-recommended@13.0.0(stylelint@15.11.0): - resolution: - { - integrity: sha512-EH+yRj6h3GAe/fRiyaoO2F9l9Tgg50AOFhaszyfov9v6ayXJ1IkSHwTxd7lB48FmOeSGDPLjatjO11fJpmarkQ==, - } - engines: { node: ^14.13.1 || >=16.0.0 } - peerDependencies: - stylelint: ^15.10.0 + stylelint-config-recommended@13.0.0(stylelint@15.11.0(typescript@4.9.5)): dependencies: stylelint: 15.11.0(typescript@4.9.5) - dev: true - /stylelint-config-standard@34.0.0(stylelint@15.11.0): - resolution: - { - integrity: sha512-u0VSZnVyW9VSryBG2LSO+OQTjN7zF9XJaAJRX/4EwkmU0R2jYwmBSN10acqZisDitS0CLiEiGjX7+Hrq8TAhfQ==, - } - engines: { node: ^14.13.1 || >=16.0.0 } - peerDependencies: - stylelint: ^15.10.0 + stylelint-config-standard@34.0.0(stylelint@15.11.0(typescript@4.9.5)): dependencies: stylelint: 15.11.0(typescript@4.9.5) - stylelint-config-recommended: 13.0.0(stylelint@15.11.0) - dev: true + stylelint-config-recommended: 13.0.0(stylelint@15.11.0(typescript@4.9.5)) - /stylelint-webpack-plugin@4.1.1(stylelint@15.11.0)(webpack@5.87.0): - resolution: - { - integrity: sha512-yOyd2AfrxfawxKDememazGVJX2vMq9o11E6HvBu4+SKvgK3ZulkjpYdI1muBTxItwoxH2UmfIZzQM+/M5V3kTQ==, - } - engines: { node: '>= 14.15.0' } - peerDependencies: - stylelint: ^13.0.0 || ^14.0.0 || ^15.0.0 - webpack: ^5.0.0 + stylelint-webpack-plugin@5.0.1(stylelint@15.11.0(typescript@4.9.5))(webpack@5.104.1): dependencies: globby: 11.1.0 jest-worker: 29.7.0 - micromatch: 4.0.5 + micromatch: 4.0.8 normalize-path: 3.0.0 - schema-utils: 4.2.0 + schema-utils: 4.3.3 stylelint: 15.11.0(typescript@4.9.5) - webpack: 5.87.0 - dev: true - - /stylelint@15.11.0(typescript@4.9.5): - resolution: - { - integrity: sha512-78O4c6IswZ9TzpcIiQJIN49K3qNoXTM8zEJzhaTE/xRTCZswaovSEVIa/uwbOltZrk16X4jAxjaOhzz/hTm1Kw==, - } - engines: { node: ^14.13.1 || >=16.0.0 } - hasBin: true + webpack: 5.104.1 + + stylelint@15.11.0(typescript@4.9.5): dependencies: '@csstools/css-parser-algorithms': 2.3.2(@csstools/css-tokenizer@2.2.1) '@csstools/css-tokenizer': 2.2.1 - '@csstools/media-query-list-parser': 2.1.5(@csstools/css-parser-algorithms@2.3.2)(@csstools/css-tokenizer@2.2.1) + '@csstools/media-query-list-parser': 2.1.5(@csstools/css-parser-algorithms@2.3.2(@csstools/css-tokenizer@2.2.1))(@csstools/css-tokenizer@2.2.1) '@csstools/selector-specificity': 3.0.0(postcss-selector-parser@6.0.13) balanced-match: 2.0.0 colord: 2.9.3 @@ -20134,152 +19437,69 @@ packages: transitivePeerDependencies: - supports-color - typescript - dev: true - - /supports-color@2.0.0: - resolution: - { - integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==, - } - engines: { node: '>=0.8.0' } - dev: true - - /supports-color@5.5.0: - resolution: - { - integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==, - } - engines: { node: '>=4' } + + supports-color@2.0.0: {} + + supports-color@5.5.0: dependencies: has-flag: 3.0.0 - /supports-color@7.2.0: - resolution: - { - integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==, - } - engines: { node: '>=8' } + supports-color@7.2.0: dependencies: has-flag: 4.0.0 - /supports-color@8.1.1: - resolution: - { - integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==, - } - engines: { node: '>=10' } + supports-color@8.1.1: dependencies: has-flag: 4.0.0 - /supports-hyperlinks@2.3.0: - resolution: - { - integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==, - } - engines: { node: '>=8' } + supports-hyperlinks@3.0.0: dependencies: has-flag: 4.0.0 supports-color: 7.2.0 - dev: true - /supports-hyperlinks@3.0.0: - resolution: - { - integrity: sha512-QBDPHyPQDRTy9ku4URNGY5Lah8PAaXs6tAAwp55sL5WCsSW7GIfdf6W5ixfziW+t7wh3GVvHyHHyQ1ESsoRvaA==, - } - engines: { node: '>=14.18' } - dependencies: - has-flag: 4.0.0 - supports-color: 7.2.0 - dev: true - - /supports-preserve-symlinks-flag@1.0.0: - resolution: - { - integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==, - } - engines: { node: '>= 0.4' } - - /svg-tags@1.0.0: - resolution: - { - integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==, - } - - /svgo@2.8.0: - resolution: - { - integrity: sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==, - } - engines: { node: '>=10.13.0' } - hasBin: true + supports-preserve-symlinks-flag@1.0.0: {} + + svg-tags@1.0.0: {} + + svgo@2.8.0: dependencies: '@trysound/sax': 0.2.0 commander: 7.2.0 css-select: 4.3.0 css-tree: 1.1.3 csso: 4.2.0 - picocolors: 1.0.0 + picocolors: 1.1.1 stable: 0.1.8 - dev: false - - /svgo@3.0.2: - resolution: - { - integrity: sha512-Z706C1U2pb1+JGP48fbazf3KxHrWOsLme6Rv7imFBn5EnuanDW1GPaA/P1/dvObE670JDePC3mnj0k0B7P0jjQ==, - } - engines: { node: '>=14.0.0' } - hasBin: true + + svgo@3.3.2: 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 - dev: false - - /symbol-tree@3.2.4: - resolution: - { - integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==, - } - dev: true - - /table@6.8.1: - resolution: - { - integrity: sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==, - } - engines: { node: '>=10.0.0' } + picocolors: 1.1.1 + + symbol-tree@3.2.4: {} + + synckit@0.11.11: + dependencies: + '@pkgr/core': 0.2.9 + + table@6.8.1: dependencies: ajv: 8.12.0 lodash.truncate: 4.4.2 slice-ansi: 4.0.0 string-width: 4.2.3 strip-ansi: 6.0.1 - dev: true - - /tapable@1.1.3: - resolution: - { - integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA==, - } - engines: { node: '>=6' } - - /tapable@2.2.1: - resolution: - { - integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==, - } - engines: { node: '>=6' } - - /tar@6.2.0: - resolution: - { - integrity: sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==, - } - engines: { node: '>=10' } + + tapable@1.1.3: {} + + tapable@2.3.0: {} + + tar@6.2.0: dependencies: chownr: 2.0.0 fs-minipass: 2.1.0 @@ -20288,13 +19508,7 @@ packages: mkdirp: 1.0.4 yallist: 4.0.0 - /teeny-request@7.2.0: - resolution: - { - integrity: sha512-SyY0pek1zWsi0LRVAALem+avzMLc33MKW/JLLakdP4s9+D7+jHcy5x6P+h94g2QNZsAqQNfX5lsbd3WSeJXrrw==, - } - engines: { node: '>=10' } - requiresBuild: true + teeny-request@7.2.0: dependencies: http-proxy-agent: 5.0.0 https-proxy-agent: 5.0.1 @@ -20304,28 +19518,9 @@ packages: transitivePeerDependencies: - encoding - supports-color - dev: false optional: true - /terminal-link@2.1.1: - resolution: - { - integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==, - } - engines: { node: '>=8' } - dependencies: - ansi-escapes: 4.3.2 - supports-hyperlinks: 2.3.0 - dev: true - - /terser-webpack-plugin@1.4.5(webpack@4.47.0): - resolution: - { - integrity: sha512-04Rfe496lN8EYruwi6oPQkG0vo8C+HT49X687FZnpPF0qMAIHONI6HEXYPKDOE8e5HjXTyKfqRd/agHtH0kOtw==, - } - engines: { node: '>= 6.9.0' } - peerDependencies: - webpack: ^4.0.0 + terser-webpack-plugin@1.4.6(webpack@4.47.0): dependencies: cacache: 12.0.4 find-cache-dir: 2.1.0 @@ -20337,16 +19532,8 @@ packages: webpack: 4.47.0 webpack-sources: 1.4.3 worker-farm: 1.7.0 - dev: false - /terser-webpack-plugin@4.2.3(webpack@4.47.0): - resolution: - { - integrity: sha512-jTgXh40RnvOrLQNgIkwEKnQ8rmHjHK4u+6UBEi+W+FPmvb+uo+chJXntKe7/3lW5mNysgSWD60KyesnhW8D6MQ==, - } - engines: { node: '>= 10.13.0' } - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 + terser-webpack-plugin@4.2.3(webpack@4.47.0): dependencies: cacache: 15.3.0 find-cache-dir: 3.3.2 @@ -20355,571 +19542,227 @@ packages: schema-utils: 3.3.0 serialize-javascript: 5.0.1 source-map: 0.6.1 - terser: 5.20.0 + terser: 5.44.1 webpack: 4.47.0 webpack-sources: 1.4.3 transitivePeerDependencies: - bluebird - dev: false - /terser-webpack-plugin@5.3.9(webpack@5.87.0): - resolution: - { - integrity: sha512-ZuXsqE07EcggTWQjXUj+Aot/OMcD0bMKGgF63f7UxYcu5/AJF53aIpK1YoP5xR9l6s/Hy2b+t1AM0bLNPRuhwA==, - } - engines: { node: '>= 10.13.0' } - peerDependencies: - '@swc/core': '*' - esbuild: '*' - uglify-js: '*' - webpack: ^5.1.0 - peerDependenciesMeta: - '@swc/core': - optional: true - esbuild: - optional: true - uglify-js: - optional: true + terser-webpack-plugin@5.3.16(webpack@5.104.1): dependencies: - '@jridgewell/trace-mapping': 0.3.19 + '@jridgewell/trace-mapping': 0.3.31 jest-worker: 27.5.1 - schema-utils: 3.3.0 - serialize-javascript: 6.0.1 - terser: 5.20.0 - webpack: 5.87.0 - - /terser@4.8.1: - resolution: - { - integrity: sha512-4GnLC0x667eJG0ewJTa6z/yXrbLGv80D9Ru6HIpCQmO+Q4PfEtBFi0ObSckqwL6VyQv/7ENJieXHo2ANmdQwgw==, - } - engines: { node: '>=6.0.0' } - hasBin: true + schema-utils: 4.3.3 + serialize-javascript: 6.0.2 + terser: 5.44.1 + webpack: 5.104.1 + + terser@4.8.1: dependencies: - acorn: 8.10.0 + acorn: 8.15.0 commander: 2.20.3 source-map: 0.6.1 source-map-support: 0.5.21 - /terser@5.20.0: - resolution: - { - integrity: sha512-e56ETryaQDyebBwJIWYB2TT6f2EZ0fL0sW/JRXNMN26zZdKi2u/E/5my5lG6jNxym6qsrVXfFRmOdV42zlAgLQ==, - } - engines: { node: '>=10' } - hasBin: true + terser@5.44.1: dependencies: - '@jridgewell/source-map': 0.3.5 - acorn: 8.10.0 + '@jridgewell/source-map': 0.3.11 + acorn: 8.15.0 commander: 2.20.3 source-map-support: 0.5.21 - /test-exclude@6.0.0: - resolution: - { - integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==, - } - engines: { node: '>=8' } + test-exclude@6.0.0: dependencies: '@istanbuljs/schema': 0.1.3 glob: 7.2.3 minimatch: 3.1.2 - dev: true - - /text-decoding@1.0.0: - resolution: - { - integrity: sha512-/0TJD42KDnVwKmDK6jj3xP7E2MG7SHAOG4tyTgyUCRPdHwvkquYNLEQltmdMa3owq3TkddCVcTsoctJI8VQNKA==, - } - requiresBuild: true - dev: false + + text-decoding@1.0.0: optional: true - /text-extensions@1.9.0: - resolution: - { - integrity: sha512-wiBrwC1EhBelW12Zy26JeOUkQ5mRu+5o8rpsJk5+2t+Y5vE7e842qtZDQ2g1NpX/29HdyFeJ4nSIhI47ENSxlQ==, - } - engines: { node: '>=0.10' } - dev: true - - /text-table@0.2.0: - resolution: - { - integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==, - } - dev: true - - /thread-loader@3.0.4(webpack@4.47.0): - resolution: - { - integrity: sha512-ByaL2TPb+m6yArpqQUZvP+5S1mZtXsEP7nWKKlAUTm7fCml8kB5s1uI3+eHRP2bk5mVYfRSBI7FFf+tWEyLZwA==, - } - engines: { node: '>= 10.13.0' } - peerDependencies: - webpack: ^4.27.0 || ^5.0.0 + text-table@0.2.0: {} + + thingies@1.21.0(tslib@2.6.2): + dependencies: + tslib: 2.6.2 + + thread-loader@3.0.4(webpack@4.47.0): dependencies: json-parse-better-errors: 1.0.2 - loader-runner: 4.3.0 + loader-runner: 4.3.1 loader-utils: 2.0.4 neo-async: 2.6.2 schema-utils: 3.3.0 webpack: 4.47.0 - dev: false - /throat@6.0.2: - resolution: - { - integrity: sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==, - } - dev: true - - /through2@2.0.5: - resolution: - { - integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==, - } + through2@2.0.5: dependencies: readable-stream: 2.3.8 xtend: 4.0.2 - dev: false - - /through2@4.0.2: - resolution: - { - integrity: sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==, - } - dependencies: - readable-stream: 3.6.2 - dev: true - /through@2.3.8: - resolution: - { - integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==, - } + through@2.3.8: {} - /time-fix-plugin@2.0.7(webpack@4.47.0): - resolution: - { - integrity: sha512-uVFet1LQToeUX0rTcSiYVYVoGuBpc8gP/2jnlUzuHMHe+gux6XLsNzxLUweabMwiUj5ejhoIMsUI55nVSEa/Vw==, - } - peerDependencies: - webpack: '>=4.0.0' + time-fix-plugin@2.0.7(webpack@4.47.0): dependencies: webpack: 4.47.0 - dev: false - /timers-browserify@2.0.12: - resolution: - { - integrity: sha512-9phl76Cqm6FhSX9Xe1ZUAMLtm1BLkKj2Qd5ApyWkXzsMRaA7dgr81kf4wJmQf/hAvg8EEyJxDo3du/0KlhPiKQ==, - } - engines: { node: '>=0.6.0' } + timers-browserify@2.0.12: dependencies: setimmediate: 1.0.5 - dev: false - /tmp@0.0.33: - resolution: - { - integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==, - } - engines: { node: '>=0.6.0' } + tinyexec@1.0.2: {} + + tldts-core@6.1.86: {} + + tldts@6.1.86: + dependencies: + tldts-core: 6.1.86 + + tmp@0.0.33: dependencies: os-tmpdir: 1.0.2 - dev: false - - /tmpl@1.0.5: - resolution: - { - integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==, - } - dev: true - - /to-arraybuffer@1.0.1: - resolution: - { - integrity: sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==, - } - dev: false - - /to-fast-properties@1.0.3: - resolution: - { - integrity: sha512-lxrWP8ejsq+7E3nNjwYmUBMAgjMTZoTI+sdBOpvNyijeDLa29LUn9QaoXAHv4+Z578hbmHHJKZknzxVtvo77og==, - } - engines: { node: '>=0.10.0' } - dev: true - - /to-fast-properties@2.0.0: - resolution: - { - integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==, - } - engines: { node: '>=4' } - - /to-object-path@0.3.0: - resolution: - { - integrity: sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==, - } - engines: { node: '>=0.10.0' } + + tmpl@1.0.5: {} + + to-arraybuffer@1.0.1: {} + + to-fast-properties@1.0.3: {} + + to-object-path@0.3.0: dependencies: kind-of: 3.2.2 - dev: false - /to-regex-range@2.1.1: - resolution: - { - integrity: sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==, - } - engines: { node: '>=0.10.0' } + to-regex-range@2.1.1: dependencies: is-number: 3.0.0 repeat-string: 1.6.1 - dev: false - /to-regex-range@5.0.1: - resolution: - { - integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==, - } - engines: { node: '>=8.0' } + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 - /to-regex@3.0.2: - resolution: - { - integrity: sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==, - } - engines: { node: '>=0.10.0' } + to-regex@3.0.2: dependencies: define-property: 2.0.2 extend-shallow: 3.0.2 regex-not: 1.0.2 safe-regex: 1.1.0 - dev: false - - /toidentifier@1.0.1: - resolution: - { - integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==, - } - engines: { node: '>=0.6' } - dev: false - - /totalist@3.0.1: - resolution: - { - integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==, - } - engines: { node: '>=6' } - dev: false - - /tough-cookie@4.1.3: - resolution: - { - integrity: sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==, - } - engines: { node: '>=6' } - dependencies: - psl: 1.9.0 - punycode: 2.3.0 - universalify: 0.2.0 - url-parse: 1.5.10 - dev: true - - /tr46@0.0.3: - resolution: - { - integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==, - } - dev: false - - /tr46@2.1.0: - resolution: - { - integrity: sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==, - } - engines: { node: '>=8' } + + toidentifier@1.0.1: {} + + totalist@3.0.1: {} + + tough-cookie@5.1.2: dependencies: - punycode: 2.3.0 - dev: true - - /trim-newlines@3.0.1: - resolution: - { - integrity: sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==, - } - engines: { node: '>=8' } - dev: true - - /trim-newlines@4.1.1: - resolution: - { - integrity: sha512-jRKj0n0jXWo6kh62nA5TEh3+4igKDXLvzBJcPpiizP7oOolUrYIxmVBG9TOtHYFHoddUk6YvAkGeGoSVTXfQXQ==, - } - engines: { node: '>=12' } - dev: true - - /ts-api-utils@1.0.3(typescript@4.9.5): - resolution: - { - integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==, - } - engines: { node: '>=16.13.0' } - peerDependencies: - typescript: '>=4.2.0' + tldts: 6.1.86 + + tr46@0.0.3: {} + + tr46@5.1.1: + dependencies: + punycode: 2.3.1 + + tree-dump@1.0.2(tslib@2.6.2): + dependencies: + tslib: 2.6.2 + + trim-newlines@4.1.1: {} + + ts-api-utils@1.0.3(typescript@4.9.5): dependencies: typescript: 4.9.5 - dev: true - - /ts-jest@27.1.1(@babel/core@7.23.2)(babel-jest@29.7.0)(jest@27.4.4)(typescript@4.9.5): - resolution: - { - integrity: sha512-Ds0VkB+cB+8g2JUmP/GKWndeZcCKrbe6jzolGrVWdqVUFByY/2KDHqxJ7yBSon7hDB1TA4PXxjfZ+JjzJisvgA==, - } - engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 } - hasBin: true - peerDependencies: - '@babel/core': '>=7.0.0-beta.0 <8' - '@types/jest': ^27.0.0 - babel-jest: '>=27.0.0 <28' - esbuild: ~0.14.0 - jest: ^27.0.0 - typescript: '>=3.8 <5.0' - peerDependenciesMeta: - '@babel/core': - optional: true - '@types/jest': - optional: true - babel-jest: - optional: true - esbuild: - optional: true + + ts-jest@29.4.6(@babel/core@7.28.4)(@jest/transform@30.2.0)(@jest/types@30.2.0)(babel-jest@30.2.0(@babel/core@7.28.4))(jest-util@30.2.0)(jest@30.2.0(@types/node@25.1.0))(typescript@4.9.5): dependencies: - '@babel/core': 7.23.2 - babel-jest: 29.7.0(@babel/core@7.23.2) bs-logger: 0.2.6 fast-json-stable-stringify: 2.1.0 - jest: 27.4.4(ts-node@10.9.1) - jest-util: 27.5.1 + handlebars: 4.7.8 + jest: 30.2.0(@types/node@25.1.0) json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 - semver: 7.5.4 + semver: 7.7.3 + type-fest: 4.41.0 typescript: 4.9.5 - yargs-parser: 20.2.9 - dev: true + yargs-parser: 21.1.1 + optionalDependencies: + '@babel/core': 7.28.4 + '@jest/transform': 30.2.0 + '@jest/types': 30.2.0 + babel-jest: 30.2.0(@babel/core@7.28.4) + jest-util: 30.2.0 - /ts-loader@8.4.0(typescript@4.2.4)(webpack@5.87.0): - resolution: - { - integrity: sha512-6nFY3IZ2//mrPc+ImY3hNWx1vCHyEhl6V+wLmL4CZcm6g1CqX7UKrkc6y0i4FwcfOhxyMPCfaEvh20f4r9GNpw==, - } - engines: { node: '>=10.0.0' } - peerDependencies: - typescript: '*' - webpack: '*' + ts-loader@8.4.0(typescript@4.9.5)(webpack@5.104.1): dependencies: chalk: 4.1.2 enhanced-resolve: 4.5.0 loader-utils: 2.0.4 - micromatch: 4.0.5 - semver: 7.5.4 - typescript: 4.2.4 - webpack: 5.87.0 - dev: true - - /ts-node@10.9.1(@types/node@20.5.1)(typescript@4.9.5): - resolution: - { - integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==, - } - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.9 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 20.5.1 - acorn: 8.10.0 - acorn-walk: 8.2.0 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 + micromatch: 4.0.8 + semver: 7.7.3 typescript: 4.9.5 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - dev: true + webpack: 5.104.1 - /ts-pnp@1.2.0(typescript@4.9.5): - resolution: - { - integrity: sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw==, - } - engines: { node: '>=6' } - peerDependencies: - typescript: '*' - peerDependenciesMeta: - typescript: - optional: true - dependencies: + ts-pnp@1.2.0(typescript@4.9.5): + optionalDependencies: typescript: 4.9.5 - dev: false - /tsconfig-paths@3.14.2: - resolution: - { - integrity: sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==, - } + tsconfig-paths@3.14.2: dependencies: '@types/json5': 0.0.29 json5: 1.0.2 minimist: 1.2.8 strip-bom: 3.0.0 - dev: true - /tsconfig@7.0.0: - resolution: - { - integrity: sha512-vZXmzPrL+EmC4T/4rVlT2jNVMWCi/O4DIiSj3UHg1OE5kCKbk4mfrXc6dZksLgRM/TZlKnousKH9bbTazUWRRw==, - } + tsconfig@7.0.0: dependencies: '@types/strip-bom': 3.0.0 '@types/strip-json-comments': 0.0.30 strip-bom: 3.0.0 strip-json-comments: 2.0.1 - dev: true - - /tslib@1.14.1: - resolution: - { - integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==, - } - dev: false - - /tslib@2.6.2: - resolution: - { - integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==, - } - dev: false - - /tty-browserify@0.0.0: - resolution: - { - integrity: sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==, - } - dev: false - - /type-check@0.4.0: - resolution: - { - integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==, - } - engines: { node: '>= 0.8.0' } + + tslib@1.14.1: {} + + tslib@2.6.2: {} + + tslib@2.8.1: + optional: true + + tty-browserify@0.0.0: {} + + tweetnacl@1.0.3: {} + + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 - dev: true - - /type-detect@4.0.8: - resolution: - { - integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==, - } - engines: { node: '>=4' } - dev: true - - /type-fest@0.18.1: - resolution: - { - integrity: sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==, - } - engines: { node: '>=10' } - dev: true - - /type-fest@0.20.2: - resolution: - { - integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==, - } - engines: { node: '>=10' } - - /type-fest@0.21.3: - resolution: - { - integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==, - } - engines: { node: '>=10' } - - /type-fest@0.6.0: - resolution: - { - integrity: sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==, - } - engines: { node: '>=8' } - dev: true - - /type-fest@0.8.1: - resolution: - { - integrity: sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==, - } - engines: { node: '>=8' } - dev: true - - /type-fest@1.4.0: - resolution: - { - integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==, - } - engines: { node: '>=10' } - dev: true - - /typed-array-buffer@1.0.0: - resolution: - { - integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==, - } - engines: { node: '>= 0.4' } + + type-detect@4.0.8: {} + + type-fest@0.20.2: {} + + type-fest@0.21.3: {} + + type-fest@0.6.0: {} + + type-fest@0.8.1: {} + + type-fest@1.4.0: {} + + type-fest@4.41.0: {} + + typed-array-buffer@1.0.0: dependencies: call-bind: 1.0.2 - get-intrinsic: 1.2.1 + get-intrinsic: 1.3.0 is-typed-array: 1.1.12 - /typed-array-byte-length@1.0.0: - resolution: - { - integrity: sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==, - } - engines: { node: '>= 0.4' } + typed-array-byte-length@1.0.0: dependencies: call-bind: 1.0.2 for-each: 0.3.3 has-proto: 1.0.1 is-typed-array: 1.1.12 - /typed-array-byte-offset@1.0.0: - resolution: - { - integrity: sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==, - } - engines: { node: '>= 0.4' } + typed-array-byte-offset@1.0.0: dependencies: available-typed-arrays: 1.0.5 call-bind: 1.0.2 @@ -20927,646 +19770,343 @@ packages: has-proto: 1.0.1 is-typed-array: 1.1.12 - /typed-array-length@1.0.4: - resolution: - { - integrity: sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==, - } + typed-array-length@1.0.4: dependencies: call-bind: 1.0.2 for-each: 0.3.3 is-typed-array: 1.1.12 - /typedarray-to-buffer@3.1.5: - resolution: - { - integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==, - } + typedarray-to-buffer@3.1.5: dependencies: is-typedarray: 1.0.0 + optional: true - /typedarray@0.0.6: - resolution: - { - integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==, - } - dev: false - - /typescript@4.2.4: - resolution: - { - integrity: sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==, - } - engines: { node: '>=4.2.0' } - hasBin: true - dev: true - - /typescript@4.9.5: - resolution: - { - integrity: sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==, - } - engines: { node: '>=4.2.0' } - hasBin: true + typedarray@0.0.6: {} - /typescript@5.2.2: - resolution: - { - integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==, - } - engines: { node: '>=14.17' } - hasBin: true - dev: true - - /ua-parser-js@1.0.36: - resolution: - { - integrity: sha512-znuyCIXzl8ciS3+y3fHJI/2OhQIXbXw9MWC/o3qwyR+RGppjZHrM27CGFSKCJXi2Kctiz537iOu2KnXs1lMQhw==, - } - dev: false - - /ufo@1.3.1: - resolution: - { - integrity: sha512-uY/99gMLIOlJPwATcMVYfqDSxUR9//AUcgZMzwfSTJPDKzA1S8mX4VLqa+fiAtveraQUBCz4FFcwVZBGbwBXIw==, - } - - /uglify-js@3.17.4: - resolution: - { - integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==, - } - engines: { node: '>=0.8.0' } - hasBin: true - dev: false + typescript@4.9.5: {} - /unbox-primitive@1.0.2: - resolution: - { - integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==, - } + ua-parser-js@1.0.38: {} + + ufo@1.6.1: {} + + uglify-js@3.19.3: + optional: true + + unbox-primitive@1.0.2: dependencies: call-bind: 1.0.2 has-bigints: 1.0.2 has-symbols: 1.0.3 which-boxed-primitive: 1.0.2 - /unctx@2.3.1: - resolution: - { - integrity: sha512-PhKke8ZYauiqh3FEMVNm7ljvzQiph0Mt3GBRve03IJm7ukfaON2OBK795tLwhbyfzknuRRkW0+Ze+CQUmzOZ+A==, - } + uncrypto@0.1.3: {} + + unctx@2.3.1: dependencies: - acorn: 8.10.0 + acorn: 8.15.0 estree-walker: 3.0.3 - magic-string: 0.30.4 - unplugin: 1.5.0 - dev: true - - /unfetch@5.0.0: - resolution: - { - integrity: sha512-3xM2c89siXg0nHvlmYsQ2zkLASvVMBisZm5lF3gFDqfF2xonNStDJyMpvaOBe0a1Edxmqrf2E0HBdmy9QyZaeg==, - } - dev: false - - /unicode-canonical-property-names-ecmascript@2.0.0: - resolution: - { - integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==, - } - engines: { node: '>=4' } - dev: false - - /unicode-match-property-ecmascript@2.0.0: - resolution: - { - integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==, - } - engines: { node: '>=4' } + magic-string: 0.30.10 + unplugin: 1.11.0 + + undici-types@7.13.0: {} + + undici-types@7.16.0: {} + + undici@6.19.7: {} + + unfetch@5.0.0: {} + + unicode-canonical-property-names-ecmascript@2.0.0: {} + + unicode-match-property-ecmascript@2.0.0: dependencies: unicode-canonical-property-names-ecmascript: 2.0.0 unicode-property-aliases-ecmascript: 2.1.0 - dev: false - - /unicode-match-property-value-ecmascript@2.1.0: - resolution: - { - integrity: sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==, - } - engines: { node: '>=4' } - dev: false - - /unicode-property-aliases-ecmascript@2.1.0: - resolution: - { - integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==, - } - engines: { node: '>=4' } - dev: false - - /unimport@3.4.0: - resolution: - { - integrity: sha512-M/lfFEgufIT156QAr/jWHLUn55kEmxBBiQsMxvRSIbquwmeJEyQYgshHDEvQDWlSJrVOOTAgnJ3FvlsrpGkanA==, - } - dependencies: - '@rollup/pluginutils': 5.0.4 + + unicode-match-property-value-ecmascript@2.1.0: {} + + unicode-property-aliases-ecmascript@2.1.0: {} + + unicorn-magic@0.1.0: {} + + unimport@3.4.0(rollup@3.30.0): + dependencies: + '@rollup/pluginutils': 5.0.4(rollup@3.30.0) escape-string-regexp: 5.0.0 fast-glob: 3.3.1 local-pkg: 0.4.3 magic-string: 0.30.4 - mlly: 1.4.2 - pathe: 1.1.1 - pkg-types: 1.0.3 - scule: 1.0.0 + mlly: 1.7.1 + pathe: 1.1.2 + pkg-types: 1.1.2 + scule: 1.3.0 strip-literal: 1.3.0 unplugin: 1.5.0 transitivePeerDependencies: - rollup - dev: true - /union-value@1.0.1: - resolution: - { - integrity: sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==, - } - engines: { node: '>=0.10.0' } + unimport@3.7.2(rollup@3.30.0): + dependencies: + '@rollup/pluginutils': 5.1.0(rollup@3.30.0) + acorn: 8.15.0 + 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.10 + mlly: 1.7.1 + pathe: 1.1.2 + pkg-types: 1.1.2 + scule: 1.3.0 + strip-literal: 2.1.0 + unplugin: 1.11.0 + transitivePeerDependencies: + - rollup + + union-value@1.0.1: dependencies: arr-union: 3.1.0 get-value: 2.0.6 is-extendable: 0.1.1 set-value: 2.0.1 - dev: false - /unique-filename@1.1.1: - resolution: - { - integrity: sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==, - } + unique-filename@1.1.1: dependencies: unique-slug: 2.0.2 - dev: false - /unique-slug@2.0.2: - resolution: - { - integrity: sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==, - } + unique-slug@2.0.2: dependencies: imurmurhash: 0.1.4 - dev: false - /unique-string@2.0.0: - resolution: - { - integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==, - } - engines: { node: '>=8' } - requiresBuild: true + unique-string@2.0.0: dependencies: crypto-random-string: 2.0.0 - dev: false optional: true - /universalify@0.1.2: - resolution: - { - integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==, - } - engines: { node: '>= 4.0.0' } - dev: false - - /universalify@0.2.0: - resolution: - { - integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==, - } - engines: { node: '>= 4.0.0' } - dev: true - - /universalify@2.0.0: - resolution: - { - integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==, - } - engines: { node: '>= 10.0.0' } - - /unpipe@1.0.0: - resolution: - { - integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==, - } - engines: { node: '>= 0.8' } - dev: false - - /unplugin@1.5.0: - resolution: - { - integrity: sha512-9ZdRwbh/4gcm1JTOkp9lAkIDrtOyOxgHmY7cjuwI8L/2RTikMcVG25GsZwNAgRuap3iDw2jeq7eoqtAsz5rW3A==, - } - dependencies: - acorn: 8.10.0 - chokidar: 3.5.3 - webpack-sources: 3.2.3 + universalify@0.1.2: {} + + universalify@2.0.0: {} + + unpipe@1.0.0: {} + + unplugin@1.11.0: + dependencies: + acorn: 8.15.0 + chokidar: 3.6.0 + webpack-sources: 3.3.3 + webpack-virtual-modules: 0.6.2 + + unplugin@1.5.0: + dependencies: + acorn: 8.15.0 + chokidar: 3.6.0 + webpack-sources: 3.3.3 webpack-virtual-modules: 0.5.0 - dev: true - /unset-value@1.0.0: - resolution: - { - integrity: sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==, - } - engines: { node: '>=0.10.0' } + unrs-resolver@1.11.1: + dependencies: + napi-postinstall: 0.3.3 + optionalDependencies: + '@unrs/resolver-binding-android-arm-eabi': 1.11.1 + '@unrs/resolver-binding-android-arm64': 1.11.1 + '@unrs/resolver-binding-darwin-arm64': 1.11.1 + '@unrs/resolver-binding-darwin-x64': 1.11.1 + '@unrs/resolver-binding-freebsd-x64': 1.11.1 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.11.1 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.11.1 + '@unrs/resolver-binding-linux-arm64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-arm64-musl': 1.11.1 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-riscv64-musl': 1.11.1 + '@unrs/resolver-binding-linux-s390x-gnu': 1.11.1 + '@unrs/resolver-binding-linux-x64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-x64-musl': 1.11.1 + '@unrs/resolver-binding-wasm32-wasi': 1.11.1 + '@unrs/resolver-binding-win32-arm64-msvc': 1.11.1 + '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 + '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 + + unset-value@1.0.0: dependencies: has-value: 0.3.1 isobject: 3.0.1 - dev: false - /untyped@1.4.0: - resolution: - { - integrity: sha512-Egkr/s4zcMTEuulcIb7dgURS6QpN7DyqQYdf+jBtiaJvQ+eRsrtWUoX84SbvQWuLkXsOjM+8sJC9u6KoMK/U7Q==, - } - hasBin: true + untyped@1.4.0: dependencies: - '@babel/core': 7.23.0 + '@babel/core': 7.28.4 '@babel/standalone': 7.23.1 - '@babel/types': 7.23.0 - defu: 6.1.2 - jiti: 1.20.0 + '@babel/types': 7.28.2 + defu: 6.1.4 + jiti: 1.21.6 mri: 1.2.0 - scule: 1.0.0 + scule: 1.3.0 transitivePeerDependencies: - supports-color - dev: true - - /upath@1.2.0: - resolution: - { - integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==, - } - engines: { node: '>=4' } - requiresBuild: true - dev: false - optional: true - /upath@2.0.1: - resolution: - { - integrity: sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==, - } - engines: { node: '>=4' } - dev: false - - /update-browserslist-db@1.0.13(browserslist@4.22.1): - resolution: - { - integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==, - } - hasBin: true - peerDependencies: - browserslist: '>= 4.21.0' + untyped@1.4.2: dependencies: - browserslist: 4.22.1 - escalade: 3.1.1 - picocolors: 1.0.0 + '@babel/core': 7.28.4 + '@babel/standalone': 7.24.7 + '@babel/types': 7.28.2 + defu: 6.1.4 + jiti: 1.21.6 + mri: 1.2.0 + scule: 1.3.0 + transitivePeerDependencies: + - supports-color - /upper-case@1.1.3: - resolution: - { - integrity: sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA==, - } - dev: false + upath@1.2.0: + optional: true + + upath@2.0.1: {} + + update-browserslist-db@1.2.3(browserslist@4.28.1): + dependencies: + browserslist: 4.28.1 + escalade: 3.2.0 + picocolors: 1.1.1 - /uri-js@4.4.1: - resolution: - { - integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==, - } + uri-js@4.4.1: dependencies: punycode: 2.3.0 - /urix@0.1.0: - resolution: - { - integrity: sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==, - } - deprecated: Please see https://github.com/lydell/urix#deprecated + urix@0.1.0: {} - /url-loader@4.1.1(file-loader@6.2.0)(webpack@4.47.0): - resolution: - { - integrity: sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA==, - } - engines: { node: '>= 10.13.0' } - peerDependencies: - file-loader: '*' - webpack: ^4.0.0 || ^5.0.0 - peerDependenciesMeta: - file-loader: - optional: true + url-loader@4.1.1(file-loader@6.2.0(webpack@5.104.1))(webpack@4.47.0): dependencies: - file-loader: 6.2.0(webpack@4.47.0) loader-utils: 2.0.4 mime-types: 2.1.35 schema-utils: 3.3.0 webpack: 4.47.0 - dev: false - - /url-parse@1.5.10: - resolution: - { - integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==, - } - dependencies: - querystringify: 2.2.0 - requires-port: 1.0.0 - dev: true + optionalDependencies: + file-loader: 6.2.0(webpack@5.104.1) - /url@0.11.3: - resolution: - { - integrity: sha512-6hxOLGfZASQK/cijlZnZJTq8OXAkt/3YGfQX45vvMYXpZoo8NdWZcY73K108Jf759lS1Bv/8wXnHDTSz17dSRw==, - } + url@0.11.3: dependencies: punycode: 1.4.1 qs: 6.11.2 - dev: false - - /use@3.1.1: - resolution: - { - integrity: sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==, - } - engines: { node: '>=0.10.0' } - dev: false - - /util-deprecate@1.0.2: - resolution: - { - integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==, - } - - /util.promisify@1.0.0: - resolution: - { - integrity: sha512-i+6qA2MPhvoKLuxnJNpXAGhg7HphQOSUq2LKMZD0m15EiskXUkMvKdF4Uui0WYeCUGea+o2cw/ZuwehtfsrNkA==, - } + + use@3.1.1: {} + + util-deprecate@1.0.2: {} + + util.promisify@1.0.0: dependencies: define-properties: 1.2.1 object.getownpropertydescriptors: 2.1.7 - dev: false - /util@0.10.4: - resolution: - { - integrity: sha512-0Pm9hTQ3se5ll1XihRic3FDIku70C+iHUdT/W926rSgHV5QgXsYbKZN8MSC3tJtSkhuROzvsQjAaFENRXr+19A==, - } + util@0.10.4: dependencies: inherits: 2.0.3 - dev: false - /util@0.11.1: - resolution: - { - integrity: sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==, - } + util@0.11.1: dependencies: inherits: 2.0.3 - dev: false - - /utila@0.4.0: - resolution: - { - integrity: sha512-Z0DbgELS9/L/75wZbro8xAnT50pBVFQZ+hUEueGDU5FN51YSCYM+jdxsfCiHjwNP/4LCDD0i/graKpeBnOXKRA==, - } - dev: false - - /utils-merge@1.0.1: - resolution: - { - integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==, - } - engines: { node: '>= 0.4.0' } - dev: false - - /uuid@8.3.2: - resolution: - { - integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==, - } - hasBin: true - requiresBuild: true - dev: false + + utila@0.4.0: {} + + utils-merge@1.0.1: {} + + uuid@8.3.2: optional: true - /v8-compile-cache-lib@3.0.1: - resolution: - { - integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==, - } - dev: true - - /v8-to-istanbul@8.1.1: - resolution: - { - integrity: sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==, - } - engines: { node: '>=10.12.0' } - dependencies: - '@types/istanbul-lib-coverage': 2.0.4 - convert-source-map: 1.9.0 - source-map: 0.7.4 - dev: true - - /validate-npm-package-license@3.0.4: - resolution: - { - integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==, - } + v8-to-istanbul@9.3.0: + dependencies: + '@jridgewell/trace-mapping': 0.3.31 + '@types/istanbul-lib-coverage': 2.0.6 + convert-source-map: 2.0.0 + + validate-npm-package-license@3.0.4: dependencies: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 - dev: true - - /vary@1.1.2: - resolution: - { - integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==, - } - engines: { node: '>= 0.8' } - dev: false - - /vite-plugin-eslint@1.8.1(eslint@8.53.0)(vite@4.4.9): - resolution: - { - integrity: sha512-PqdMf3Y2fLO9FsNPmMX+//2BF5SF8nEWspZdgl4kSt7UvHDRHVVfHvxsD7ULYzZrJDGRxR81Nq7TOFgwMnUang==, - } - peerDependencies: - eslint: '>=7' - vite: '>=2' + + vary@1.1.2: {} + + vite-plugin-eslint@1.8.1(eslint@8.57.1)(vite@4.5.3(@types/node@25.1.0)(sass@1.32.13)(terser@5.44.1)): dependencies: '@rollup/pluginutils': 4.2.1 '@types/eslint': 8.44.3 - eslint: 8.53.0 - rollup: 2.79.1 - vite: 4.4.9(@types/node@20.5.1) - dev: true - - /vite-plugin-stylelint@4.3.0(postcss@8.4.31)(stylelint@15.11.0)(vite@4.4.9): - resolution: - { - integrity: sha512-S8BONq5X8TndOFt+My4lkeHxVZvkDQRL++TV0nvnuYgOU/CvDddPPOT4V6go+ETzWK0NEtXqCGFnpkmm8c8Xcg==, - } - engines: { node: '>=14.18' } - peerDependencies: - '@types/stylelint': ^13.0.0 - postcss: ^7.0.0 || ^8.0.0 - rollup: ^2.0.0 || ^3.0.0 - stylelint: ^13.0.0 || ^14.0.0 || ^15.0.0 - vite: ^2.0.0 || ^3.0.0 || ^4.0.0 - peerDependenciesMeta: - '@types/stylelint': - optional: true - postcss: - optional: true - rollup: - optional: true + eslint: 8.57.1 + rollup: 2.79.2 + vite: 4.5.3(@types/node@25.1.0)(sass@1.32.13)(terser@5.44.1) + + vite-plugin-stylelint@5.3.1(postcss@8.5.6)(rollup@3.30.0)(stylelint@15.11.0(typescript@4.9.5))(vite@4.5.3(@types/node@25.1.0)(sass@1.32.13)(terser@5.44.1)): dependencies: - '@rollup/pluginutils': 5.0.4 - chokidar: 3.5.3 - postcss: 8.4.31 + '@rollup/pluginutils': 5.1.0(rollup@3.30.0) + chokidar: 3.6.0 + debug: 4.4.1 stylelint: 15.11.0(typescript@4.9.5) - vite: 4.4.9(@types/node@20.5.1) - dev: true - - /vite@4.4.9(@types/node@20.5.1): - resolution: - { - integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==, - } - engines: { node: ^14.18.0 || >=16.0.0 } - hasBin: true - peerDependencies: - '@types/node': '>= 14' - less: '*' - lightningcss: ^1.21.0 - sass: '*' - stylus: '*' - sugarss: '*' - terser: ^5.4.0 - peerDependenciesMeta: - '@types/node': - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true + vite: 4.5.3(@types/node@25.1.0)(sass@1.32.13)(terser@5.44.1) + optionalDependencies: + postcss: 8.5.6 + rollup: 3.30.0 + transitivePeerDependencies: + - supports-color + + vite@4.5.3(@types/node@25.1.0)(sass@1.32.13)(terser@5.44.1): dependencies: - '@types/node': 20.5.1 esbuild: 0.18.20 - postcss: 8.4.31 - rollup: 3.29.4 + postcss: 8.5.10 + rollup: 3.30.0 optionalDependencies: + '@types/node': 25.1.0 fsevents: 2.3.3 - dev: true + sass: 1.32.13 + terser: 5.44.1 - /vm-browserify@1.1.2: - resolution: - { - integrity: sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ==, - } - dev: false + vm-browserify@1.1.2: {} - /vue-chartjs@5.2.0(chart.js@4.4.0)(vue@2.7.15): - resolution: - { - integrity: sha512-d3zpKmGZr2OWHQ1xmxBcAn5ShTG917+/UCLaSpaCDDqT0U7DBsvFzTs69ZnHCgKoXT55GZDW8YEj9Av+dlONLA==, - } - peerDependencies: - chart.js: ^4.1.1 - vue: ^3.0.0-0 || ^2.7.0 + vue-chartjs@5.3.3(chart.js@4.5.1)(vue@2.7.16): dependencies: - chart.js: 4.4.0 - vue: 2.7.15 - dev: false + chart.js: 4.5.1 + vue: 2.7.16 - /vue-class-component@7.2.6(vue@2.7.15): - resolution: - { - integrity: sha512-+eaQXVrAm/LldalI272PpDe3+i4mPis0ORiMYxF6Ae4hyuCh15W8Idet7wPUEs4N4YptgFHGys4UrgNQOMyO6w==, - } - peerDependencies: - vue: ^2.0.0 + vue-class-component@7.2.6(vue@2.7.16): dependencies: - vue: 2.7.15 - dev: false + vue: 2.7.16 - /vue-client-only@2.1.0: - resolution: - { - integrity: sha512-vKl1skEKn8EK9f8P2ZzhRnuaRHLHrlt1sbRmazlvsx6EiC3A8oWF8YCBrMJzoN+W3OnElwIGbVjsx6/xelY1AA==, - } + vue-client-only@2.1.0: {} - /vue-eslint-parser@9.3.1(eslint@8.53.0): - resolution: - { - integrity: sha512-Clr85iD2XFZ3lJ52/ppmUDG/spxQu6+MAeHXjjyI4I1NUYZ9xmenQp4N0oaHJhrA8OOxltCVxMRfANGa70vU0g==, - } - engines: { node: ^14.17.0 || >=16.0.0 } - peerDependencies: - eslint: '>=6.0.0' + vue-eslint-parser@9.3.1(eslint@8.57.1): dependencies: - debug: 4.3.4 - eslint: 8.53.0 + debug: 4.4.1 + eslint: 8.57.1 eslint-scope: 7.2.2 eslint-visitor-keys: 3.4.3 espree: 9.6.1 esquery: 1.5.0 lodash: 4.17.21 - semver: 7.5.4 + semver: 7.7.3 transitivePeerDependencies: - supports-color - dev: true - - /vue-glow@1.4.2: - resolution: - { - integrity: sha512-MDC5Q817fH51OhCpYopAcXwMZ49yVAjEgiJ1sXlc3Kyul0AU343AbB0zflr+LnuiuS/EegfVkxYh0I67xSMYZw==, - } - dependencies: - vue: 2.7.15 - dev: false - - /vue-hot-reload-api@2.3.4: - resolution: - { - integrity: sha512-BXq3jwIagosjgNVae6tkHzzIk6a8MHFtzAdwhnV5VlvPTFxDCvIttgSiHWjdGoTJvXtmRu5HacExfdarRcFhog==, - } - dev: false - - /vue-jest@3.0.4(babel-core@7.0.0-bridge.0)(vue-template-compiler@2.7.15)(vue@2.7.15): - resolution: - { - integrity: sha512-PY9Rwt4OyaVlA+KDJJ0614CbEvNOkffDI9g9moLQC/2DDoo0YrqZm7dHi13Q10uoK5Nt5WCYFdeAheOExPah0w==, - } - peerDependencies: - babel-core: ^6.25.0 || ^7.0.0-0 - vue: ^2.x - vue-template-compiler: ^2.x + + vue-eslint-parser@9.4.3(eslint@8.57.1): + dependencies: + debug: 4.4.1 + eslint: 8.57.1 + eslint-scope: 7.2.2 + eslint-visitor-keys: 3.4.3 + espree: 9.6.1 + esquery: 1.6.0 + lodash: 4.17.21 + semver: 7.7.3 + transitivePeerDependencies: + - supports-color + + vue-glow@1.4.2: + dependencies: + vue: 2.7.16 + + vue-hot-reload-api@2.3.4: {} + + vue-jest@3.0.7(babel-core@7.0.0-bridge.0(@babel/core@7.28.4))(vue-template-compiler@2.7.16)(vue@2.7.16): dependencies: - babel-core: 7.0.0-bridge.0(@babel/core@7.23.2) + babel-core: 7.0.0-bridge.0(@babel/core@7.28.4) babel-plugin-transform-es2015-modules-commonjs: 6.26.2 chalk: 2.4.2 + deasync: 0.1.29 extract-from-css: 0.4.4 find-babel-config: 1.2.0 js-beautify: 1.14.9 @@ -21574,45 +20114,25 @@ packages: object-assign: 4.1.1 source-map: 0.5.7 tsconfig: 7.0.0 - vue: 2.7.15 - vue-template-compiler: 2.7.15 + vue: 2.7.16 + vue-template-compiler: 2.7.16 vue-template-es2015-compiler: 1.9.1 transitivePeerDependencies: - supports-color - dev: true - /vue-loader@15.11.1(babel-core@7.0.0-bridge.0)(cache-loader@4.1.0)(css-loader@5.2.7)(lodash@4.17.21)(prettier@3.0.3)(vue-template-compiler@2.7.15)(webpack@4.47.0): - resolution: - { - integrity: sha512-0iw4VchYLePqJfJu9s62ACWUXeSqM30SQqlIftbYWM3C+jpPcEHKSPUZBLjSF9au4HTHQ/naF6OGnO3Q/qGR3Q==, - } - peerDependencies: - '@vue/compiler-sfc': ^3.0.8 - cache-loader: '*' - css-loader: '*' - prettier: '*' - vue-template-compiler: '*' - webpack: ^3.0.0 || ^4.1.0 || ^5.0.0-0 - peerDependenciesMeta: - '@vue/compiler-sfc': - optional: true - cache-loader: - optional: true - prettier: - optional: true - vue-template-compiler: - optional: true + vue-loader@15.11.1(babel-core@7.0.0-bridge.0(@babel/core@7.28.4))(cache-loader@4.1.0(webpack@4.47.0))(css-loader@5.2.7(webpack@5.104.1))(ejs@3.1.10)(handlebars@4.7.8)(lodash@4.17.21)(prettier@3.8.1)(vue-template-compiler@2.7.16)(webpack@4.47.0): dependencies: - '@vue/component-compiler-utils': 3.3.0(babel-core@7.0.0-bridge.0)(lodash@4.17.21) - cache-loader: 4.1.0(webpack@4.47.0) - css-loader: 5.2.7(webpack@4.47.0) + '@vue/component-compiler-utils': 3.3.0(babel-core@7.0.0-bridge.0(@babel/core@7.28.4))(ejs@3.1.10)(handlebars@4.7.8)(lodash@4.17.21) + css-loader: 5.2.7(webpack@5.104.1) hash-sum: 1.0.2 loader-utils: 1.4.2 - prettier: 3.0.3 vue-hot-reload-api: 2.3.4 vue-style-loader: 4.1.3 - vue-template-compiler: 2.7.15 webpack: 4.47.0 + optionalDependencies: + cache-loader: 4.1.0(webpack@4.47.0) + prettier: 3.8.1 + vue-template-compiler: 2.7.16 transitivePeerDependencies: - arc-templates - atpl @@ -21667,67 +20187,23 @@ packages: - velocityjs - walrus - whiskers - dev: false - /vue-meta@2.4.0: - resolution: - { - integrity: sha512-XEeZUmlVeODclAjCNpWDnjgw+t3WA6gdzs6ENoIAgwO1J1d5p1tezDhtteLUFwcaQaTtayRrsx7GL6oXp/m2Jw==, - } + vue-meta@2.4.0: dependencies: deepmerge: 4.3.1 - /vue-no-ssr@1.1.1: - resolution: - { - integrity: sha512-ZMjqRpWabMPqPc7gIrG0Nw6vRf1+itwf0Itft7LbMXs2g3Zs/NFmevjZGN1x7K3Q95GmIjWbQZTVerxiBxI+0g==, - } - - /vue-property-decorator@9.1.2(vue-class-component@7.2.6)(vue@2.7.15): - resolution: - { - integrity: sha512-xYA8MkZynPBGd/w5QFJ2d/NM0z/YeegMqYTphy7NJQXbZcuU6FC6AOdUAcy4SXP+YnkerC6AfH+ldg7PDk9ESQ==, - } - peerDependencies: - vue: '*' - vue-class-component: '*' - dependencies: - vue: 2.7.15 - vue-class-component: 7.2.6(vue@2.7.15) - dev: false + vue-no-ssr@1.1.1: {} - /vue-router@3.6.5(vue@2.7.15): - resolution: - { - integrity: sha512-VYXZQLtjuvKxxcshuRAwjHnciqZVoXAjTjcqBTz4rKc8qih9g9pI3hbDjmqXaHdgL3v8pV6P8Z335XvHzESxLQ==, - } - peerDependencies: - vue: ^2 + vue-property-decorator@9.1.2(vue-class-component@7.2.6(vue@2.7.16))(vue@2.7.16): dependencies: - vue: 2.7.15 - dev: false + vue: 2.7.16 + vue-class-component: 7.2.6(vue@2.7.16) - /vue-server-renderer@2.7.14: - resolution: - { - integrity: sha512-NlGFn24tnUrj7Sqb8njhIhWREuCJcM3140aMunLNcx951BHG8j3XOrPP7psSCaFA8z6L4IWEjudztdwTp1CBVw==, - } + vue-router@3.6.5(vue@2.7.16): dependencies: - chalk: 4.1.2 - hash-sum: 2.0.0 - he: 1.2.0 - lodash.template: 4.5.0 - lodash.uniq: 4.5.0 - resolve: 1.22.6 - serialize-javascript: 6.0.1 - source-map: 0.5.6 - dev: false + vue: 2.7.16 - /vue-server-renderer@2.7.15: - resolution: - { - integrity: sha512-5Wy6ls7ErawmgxlogoScTDOQzqBp4+B9CKV1Dl4280xVPBs1+iHpghW1nlKNd1JWKI3O2s4X4vwmg1C7Rvy7oA==, - } + vue-server-renderer@2.7.16: dependencies: chalk: 4.1.2 hash-sum: 2.0.0 @@ -21737,288 +20213,124 @@ packages: resolve: 1.22.6 serialize-javascript: 6.0.1 source-map: 0.5.6 - dev: false - /vue-style-loader@4.1.3: - resolution: - { - integrity: sha512-sFuh0xfbtpRlKfm39ss/ikqs9AbKCoXZBpHeVZ8Tx650o0k0q/YCM7FRvigtxpACezfq6af+a7JeqVTWvncqDg==, - } + vue-style-loader@4.1.3: dependencies: hash-sum: 1.0.2 loader-utils: 1.4.2 - dev: false - /vue-template-compiler@2.7.15: - resolution: - { - integrity: sha512-yQxjxMptBL7UAog00O8sANud99C6wJF+7kgbcwqkvA38vCGF7HWE66w0ZFnS/kX5gSoJr/PQ4/oS3Ne2pW37Og==, - } + vue-template-compiler@2.7.16: dependencies: de-indent: 1.0.2 he: 1.2.0 - /vue-template-es2015-compiler@1.9.1: - resolution: - { - integrity: sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==, - } + vue-template-es2015-compiler@1.9.1: {} - /vue@2.7.15: - resolution: - { - integrity: sha512-a29fsXd2G0KMRqIFTpRgpSbWaNBK3lpCTOLuGLEDnlHWdjB8fwl6zyYZ8xCrqkJdatwZb4mGHiEfJjnw0Q6AwQ==, - } + vue@2.7.16: dependencies: - '@vue/compiler-sfc': 2.7.15 + '@vue/compiler-sfc': 2.7.16 csstype: 3.1.2 - /vuetify-loader@1.9.2(vue@2.7.15)(vuetify@2.7.1)(webpack@5.87.0): - resolution: - { - integrity: sha512-8PP2w7aAs/rjA+Izec6qY7sHVb75MNrGQrDOTZJ5IEnvl+NiFhVpU2iWdRDZ3eMS842cWxSWStvkr+KJJKy+Iw==, - } - peerDependencies: - gm: ^1.23.0 - pug: ^2.0.0 || ^3.0.0 - sharp: '*' - vue: ^2.7.2 - vuetify: ^1.3.0 || ^2.0.0 - webpack: ^4.0.0 || ^5.0.0 - peerDependenciesMeta: - gm: - optional: true - pug: - optional: true - sharp: - optional: true + vuetify-loader@1.9.2(vue@2.7.16)(vuetify@2.7.2(vue@2.7.16))(webpack@5.104.1): dependencies: - acorn: 8.10.0 + acorn: 8.15.0 acorn-walk: 8.2.0 decache: 4.6.2 - file-loader: 6.2.0(webpack@5.87.0) + file-loader: 6.2.0(webpack@5.104.1) loader-utils: 2.0.4 - vue: 2.7.15 - vuetify: 2.7.1(vue@2.7.15) - webpack: 5.87.0 - dev: true - - /vuetify@2.7.1(vue@2.7.15): - resolution: - { - integrity: sha512-DVFmRsDtYrITw9yuGLwpFWngFYzEgk0KwloDCIV3+vhZw+NBFJOSzdbttbYmOwtqvQlhDxUyIRQolrRbSFAKlg==, - } - peerDependencies: - vue: ^2.6.4 - dependencies: - vue: 2.7.15 + vue: 2.7.16 + vuetify: 2.7.2(vue@2.7.16) + webpack: 5.104.1 - /vuex@3.6.2(vue@2.7.15): - resolution: - { - integrity: sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==, - } - peerDependencies: - vue: ^2.0.0 + vuetify@2.7.2(vue@2.7.16): dependencies: - vue: 2.7.15 - dev: false + vue: 2.7.16 - /w3c-hr-time@1.0.2: - resolution: - { - integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==, - } - deprecated: Use your platform's native performance.now() and performance.timeOrigin. + vuex@3.6.2(vue@2.7.16): dependencies: - browser-process-hrtime: 1.0.0 - dev: true + vue: 2.7.16 - /w3c-xmlserializer@2.0.0: - resolution: - { - integrity: sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==, - } - engines: { node: '>=10' } + w3c-xmlserializer@5.0.0: dependencies: - xml-name-validator: 3.0.0 - dev: true + xml-name-validator: 5.0.0 - /walker@1.0.8: - resolution: - { - integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==, - } + walker@1.0.8: dependencies: makeerror: 1.0.12 - dev: true - /watchpack-chokidar2@2.0.1: - resolution: - { - integrity: sha512-nCFfBIPKr5Sh61s4LPpy1Wtfi0HE8isJ3d2Yb5/Ppw2P2B/3eVSEBjKfN0fmHJSK14+31KwMKmcrzs2GM4P0Ww==, - } - requiresBuild: true + watchpack-chokidar2@2.0.1: dependencies: chokidar: 2.1.8 transitivePeerDependencies: - supports-color - dev: false optional: true - /watchpack@1.7.5: - resolution: - { - integrity: sha512-9P3MWk6SrKjHsGkLT2KHXdQ/9SNkyoJbabxnKOoJepsvJjJG8uYTR3yTPxPQvNDI3w4Nz1xnE0TLHK4RIVe/MQ==, - } + watchpack@1.7.5: dependencies: graceful-fs: 4.2.11 neo-async: 2.6.2 optionalDependencies: - chokidar: 3.5.3 + chokidar: 3.6.0 watchpack-chokidar2: 2.0.1 transitivePeerDependencies: - supports-color - dev: false - /watchpack@2.4.0: - resolution: - { - integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==, - } - engines: { node: '>=10.13.0' } + watchpack@2.5.0: dependencies: glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 - /webidl-conversions@3.0.1: - resolution: - { - integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==, - } - dev: false - - /webidl-conversions@5.0.0: - resolution: - { - integrity: sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==, - } - engines: { node: '>=8' } - dev: true - - /webidl-conversions@6.1.0: - resolution: - { - integrity: sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==, - } - engines: { node: '>=10.4' } - dev: true - - /webpack-bundle-analyzer@4.9.1: - resolution: - { - integrity: sha512-jnd6EoYrf9yMxCyYDPj8eutJvtjQNp8PHmni/e/ulydHBWhT5J3menXt3HEkScsu9YqMAcG4CfFjs3rj5pVU1w==, - } - engines: { node: '>= 10.13.0' } - hasBin: true + webidl-conversions@3.0.1: {} + + webidl-conversions@7.0.0: {} + + webpack-bundle-analyzer@4.10.2: dependencies: '@discoveryjs/json-ext': 0.5.7 - acorn: 8.10.0 + acorn: 8.15.0 acorn-walk: 8.2.0 commander: 7.2.0 + debounce: 1.2.1 escape-string-regexp: 4.0.0 gzip-size: 6.0.0 - is-plain-object: 5.0.0 - lodash.debounce: 4.0.8 - lodash.escape: 4.0.1 - lodash.flatten: 4.4.0 - lodash.invokemap: 4.6.0 - lodash.pullall: 4.2.0 - lodash.uniqby: 4.7.0 + html-escaper: 2.0.2 opener: 1.5.2 - picocolors: 1.0.0 + picocolors: 1.1.1 sirv: 2.0.3 - ws: 7.5.9 + ws: 7.5.10 transitivePeerDependencies: - bufferutil - utf-8-validate - dev: false - /webpack-dev-middleware@5.3.3(webpack@4.47.0): - resolution: - { - integrity: sha512-hj5CYrY0bZLB+eTO+x/j67Pkrquiy7kWepMHmUMoPsmcUaeEnQJqFzHJOyxgWlq746/wUuA64p9ta34Kyb01pA==, - } - engines: { node: '>= 12.13.0' } - peerDependencies: - webpack: ^4.0.0 || ^5.0.0 + webpack-dev-middleware@5.3.4(webpack@4.47.0): dependencies: colorette: 2.0.20 memfs: 3.5.3 mime-types: 2.1.35 range-parser: 1.2.1 - schema-utils: 4.2.0 + schema-utils: 4.3.3 webpack: 4.47.0 - dev: false - /webpack-hot-middleware@2.25.4: - resolution: - { - integrity: sha512-IRmTspuHM06aZh98OhBJtqLpeWFM8FXJS5UYpKYxCJzyFoyWj1w6VGFfomZU7OPA55dMLrQK0pRT1eQ3PACr4w==, - } + webpack-hot-middleware@2.26.1: dependencies: ansi-html-community: 0.0.8 html-entities: 2.4.0 strip-ansi: 6.0.1 - dev: false - - /webpack-node-externals@3.0.0: - resolution: - { - integrity: sha512-LnL6Z3GGDPht/AigwRh2dvL9PQPFQ8skEpVrWZXLWBYmqcaojHNN0onvHzie6rq7EWKrrBfPYqNEzTJgiwEQDQ==, - } - engines: { node: '>=6' } - dev: false - - /webpack-sources@1.4.3: - resolution: - { - integrity: sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==, - } + + webpack-node-externals@3.0.0: {} + + webpack-sources@1.4.3: dependencies: source-list-map: 2.0.1 source-map: 0.6.1 - dev: false - - /webpack-sources@3.2.3: - resolution: - { - integrity: sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w==, - } - engines: { node: '>=10.13.0' } - - /webpack-virtual-modules@0.5.0: - resolution: - { - integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==, - } - dev: true - - /webpack@4.47.0: - resolution: - { - integrity: sha512-td7fYwgLSrky3fI1EuU5cneU4+pbH6GgOfuKNS1tNPcfdGinGELAqsb/BP4nnvZyKSG2i/xFGU7+n2PvZA8HJQ==, - } - engines: { node: '>=6.11.5' } - hasBin: true - peerDependencies: - webpack-cli: '*' - webpack-command: '*' - peerDependenciesMeta: - webpack-cli: - optional: true - webpack-command: - optional: true + + webpack-sources@3.3.3: {} + + webpack-virtual-modules@0.5.0: {} + + webpack-virtual-modules@0.6.2: {} + + webpack@4.47.0: dependencies: '@webassemblyjs/ast': 1.9.0 '@webassemblyjs/helper-module-context': 1.9.0 @@ -22027,7 +20339,7 @@ packages: acorn: 6.4.2 ajv: 6.12.6 ajv-keywords: 3.5.2(ajv@6.12.6) - chrome-trace-event: 1.0.3 + chrome-trace-event: 1.0.4 enhanced-resolve: 4.5.0 eslint-scope: 4.0.3 json-parse-better-errors: 1.0.2 @@ -22040,134 +20352,81 @@ packages: node-libs-browser: 2.2.1 schema-utils: 1.0.0 tapable: 1.1.3 - terser-webpack-plugin: 1.4.5(webpack@4.47.0) + terser-webpack-plugin: 1.4.6(webpack@4.47.0) watchpack: 1.7.5 webpack-sources: 1.4.3 transitivePeerDependencies: - supports-color - dev: false - - /webpack@5.87.0: - resolution: - { - integrity: sha512-GOu1tNbQ7p1bDEoFRs2YPcfyGs8xq52yyPBZ3m2VGnXGtV9MxjrkABHm4V9Ia280OefsSLzvbVoXcfLxjKY/Iw==, - } - engines: { node: '>=10.13.0' } - hasBin: true - peerDependencies: - webpack-cli: '*' - peerDependenciesMeta: - webpack-cli: - optional: true - dependencies: - '@types/eslint-scope': 3.7.5 - '@types/estree': 1.0.2 - '@webassemblyjs/ast': 1.11.6 - '@webassemblyjs/wasm-edit': 1.11.6 - '@webassemblyjs/wasm-parser': 1.11.6 - acorn: 8.10.0 - acorn-import-assertions: 1.9.0(acorn@8.10.0) - browserslist: 4.22.1 - chrome-trace-event: 1.0.3 - enhanced-resolve: 5.15.0 - es-module-lexer: 1.3.1 + + webpack@5.104.1: + dependencies: + '@types/eslint-scope': 3.7.7 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + '@webassemblyjs/ast': 1.14.1 + '@webassemblyjs/wasm-edit': 1.14.1 + '@webassemblyjs/wasm-parser': 1.14.1 + acorn: 8.15.0 + acorn-import-phases: 1.0.4(acorn@8.15.0) + browserslist: 4.28.1 + chrome-trace-event: 1.0.4 + enhanced-resolve: 5.18.4 + es-module-lexer: 2.0.0 eslint-scope: 5.1.1 events: 3.3.0 glob-to-regexp: 0.4.1 graceful-fs: 4.2.11 json-parse-even-better-errors: 2.3.1 - loader-runner: 4.3.0 + loader-runner: 4.3.1 mime-types: 2.1.35 neo-async: 2.6.2 - schema-utils: 3.3.0 - tapable: 2.2.1 - terser-webpack-plugin: 5.3.9(webpack@5.87.0) - watchpack: 2.4.0 - webpack-sources: 3.2.3 + schema-utils: 4.3.3 + tapable: 2.3.0 + terser-webpack-plugin: 5.3.16(webpack@5.104.1) + watchpack: 2.5.0 + webpack-sources: 3.3.3 transitivePeerDependencies: - '@swc/core' - esbuild - uglify-js - /webpackbar@5.0.2(webpack@4.47.0): - resolution: - { - integrity: sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ==, - } - engines: { node: '>=12' } - peerDependencies: - webpack: 3 || 4 || 5 + webpackbar@6.0.1(webpack@4.47.0): dependencies: + ansi-escapes: 4.3.2 chalk: 4.1.2 - consola: 2.15.3 + consola: 3.2.3 + figures: 3.2.0 + markdown-table: 2.0.0 pretty-time: 1.1.0 - std-env: 3.4.3 + std-env: 3.7.0 webpack: 4.47.0 - dev: false + wrap-ansi: 7.0.0 - /websocket-driver@0.7.4: - resolution: - { - integrity: sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==, - } - engines: { node: '>=0.8.0' } + websocket-driver@0.7.4: dependencies: http-parser-js: 0.5.8 safe-buffer: 5.2.1 websocket-extensions: 0.1.4 - dev: false - - /websocket-extensions@0.1.4: - resolution: - { - integrity: sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==, - } - engines: { node: '>=0.8.0' } - dev: false - - /whatwg-encoding@1.0.5: - resolution: - { - integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==, - } + + websocket-extensions@0.1.4: {} + + whatwg-encoding@3.1.1: dependencies: - iconv-lite: 0.4.24 - dev: true + iconv-lite: 0.6.3 - /whatwg-mimetype@2.3.0: - resolution: - { - integrity: sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==, - } - dev: true + whatwg-mimetype@4.0.0: {} - /whatwg-url@5.0.0: - resolution: - { - integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==, - } + whatwg-url@14.2.0: dependencies: - tr46: 0.0.3 - webidl-conversions: 3.0.1 - dev: false + tr46: 5.1.1 + webidl-conversions: 7.0.0 - /whatwg-url@8.7.0: - resolution: - { - integrity: sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==, - } - engines: { node: '>=10' } + whatwg-url@5.0.0: dependencies: - lodash: 4.17.21 - tr46: 2.1.0 - webidl-conversions: 6.1.0 - dev: true + tr46: 0.0.3 + webidl-conversions: 3.0.1 - /which-boxed-primitive@1.0.2: - resolution: - { - integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==, - } + which-boxed-primitive@1.0.2: dependencies: is-bigint: 1.0.4 is-boolean-object: 1.1.2 @@ -22175,137 +20434,80 @@ packages: is-string: 1.0.7 is-symbol: 1.0.4 - /which-typed-array@1.1.11: - resolution: - { - integrity: sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==, - } - engines: { node: '>= 0.4' } + which-module@2.0.1: {} + + which-typed-array@1.1.11: dependencies: available-typed-arrays: 1.0.5 call-bind: 1.0.2 for-each: 0.3.3 gopd: 1.0.1 - has-tostringtag: 1.0.0 + has-tostringtag: 1.0.2 - /which@1.3.1: - resolution: - { - integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==, - } - hasBin: true + which@1.3.1: dependencies: isexe: 2.0.0 - dev: true - - /which@2.0.2: - resolution: - { - integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==, - } - engines: { node: '>= 8' } - hasBin: true + + which@2.0.2: dependencies: isexe: 2.0.0 - /widest-line@3.1.0: - resolution: - { - integrity: sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==, - } - engines: { node: '>=8' } + widest-line@3.1.0: dependencies: string-width: 4.2.3 - dev: false - /worker-farm@1.7.0: - resolution: - { - integrity: sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==, - } + wordwrap@1.0.0: {} + + worker-farm@1.7.0: dependencies: errno: 0.1.8 - dev: false - /wrap-ansi@7.0.0: - resolution: - { - integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==, - } - engines: { node: '>=10' } + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 - /wrap-ansi@8.1.0: - resolution: - { - integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==, - } - engines: { node: '>=12' } + wrap-ansi@8.1.0: dependencies: - ansi-styles: 6.2.1 + ansi-styles: 6.2.3 string-width: 5.1.2 + strip-ansi: 7.1.2 + + wrap-ansi@9.0.0: + dependencies: + ansi-styles: 6.2.3 + string-width: 7.2.0 strip-ansi: 7.1.0 - dev: true - /wrappy@1.0.2: - resolution: - { - integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==, - } + wrappy@1.0.2: {} - /write-file-atomic@2.4.3: - resolution: - { - integrity: sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ==, - } + write-file-atomic@2.4.3: dependencies: graceful-fs: 4.2.11 imurmurhash: 0.1.4 signal-exit: 3.0.7 - dev: false - /write-file-atomic@3.0.3: - resolution: - { - integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==, - } + write-file-atomic@3.0.3: dependencies: imurmurhash: 0.1.4 is-typedarray: 1.0.0 signal-exit: 3.0.7 typedarray-to-buffer: 3.1.5 + optional: true - /write-file-atomic@4.0.2: - resolution: - { - integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==, - } - engines: { node: ^12.13.0 || ^14.15.0 || >=16.0.0 } - dependencies: - imurmurhash: 0.1.4 - signal-exit: 3.0.7 - dev: true - - /write-file-atomic@5.0.1: - resolution: - { - integrity: sha512-+QU2zd6OTD8XWIJCbffaiQeH9U73qIqafo1x6V1snCWYGJf6cVE0cDR4D8xRzcEnfI21IFrUPzPGtcPf8AC+Rw==, - } - engines: { node: ^14.17.0 || ^16.13.0 || >=18.0.0 } + write-file-atomic@5.0.1: dependencies: imurmurhash: 0.1.4 signal-exit: 4.1.0 - dev: true - /write-json-file@2.3.0: - resolution: - { - integrity: sha512-84+F0igFp2dPD6UpAQjOUX3CdKUOqUzn6oE9sDBNzUXINR5VceJ1rauZltqQB/bcYsx3EpKys4C7/PivKUAiWQ==, - } - engines: { node: '>=4' } + write-json-file@2.3.0: dependencies: detect-indent: 5.0.0 graceful-fs: 4.2.11 @@ -22313,184 +20515,84 @@ packages: pify: 3.0.0 sort-keys: 2.0.0 write-file-atomic: 2.4.3 - dev: false - /ws@7.5.9: - resolution: - { - integrity: sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==, - } - engines: { node: '>=8.3.0' } - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true + ws@7.5.10: {} + + ws@8.18.3: {} - /xdg-basedir@4.0.0: - resolution: - { - integrity: sha512-PSNhEJDejZYV7h50BohL09Er9VaIefr2LMAf3OEmpCkjOi34eYyQYAXUTjEQtZJTKcF0E2UKTh+osDLsgNim9Q==, - } - engines: { node: '>=8' } - requiresBuild: true - dev: false + xdg-basedir@4.0.0: optional: true - /xml-name-validator@3.0.0: - resolution: - { - integrity: sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==, - } - dev: true - - /xml-name-validator@4.0.0: - resolution: - { - integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==, - } - engines: { node: '>=12' } - dev: true - - /xmlbuilder@13.0.2: - resolution: - { - integrity: sha512-Eux0i2QdDYKbdbA6AM6xE4m6ZTZr4G4xF9kahI2ukSEMCzwce2eX9WlTI5J3s+NU7hpasFsr8hWIONae7LluAQ==, - } - engines: { node: '>=6.0' } - dev: false - - /xmlchars@2.2.0: - resolution: - { - integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==, - } - dev: true - - /xtend@4.0.2: - resolution: - { - integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==, - } - engines: { node: '>=0.4' } - dev: false - - /xxhashjs@0.2.2: - resolution: - { - integrity: sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw==, - } + xml-name-validator@4.0.0: {} + + xml-name-validator@5.0.0: {} + + xmlbuilder@13.0.2: {} + + xmlchars@2.2.0: {} + + xtend@4.0.2: {} + + xxhashjs@0.2.2: dependencies: cuint: 0.2.2 - dev: false - - /y18n@4.0.3: - resolution: - { - integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==, - } - dev: false - - /y18n@5.0.8: - resolution: - { - integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==, - } - engines: { node: '>=10' } - - /yallist@2.1.2: - resolution: - { - integrity: sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==, - } - dev: false - - /yallist@3.1.1: - resolution: - { - integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==, - } - - /yallist@4.0.0: - resolution: - { - integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==, - } - - /yaml@1.10.2: - resolution: - { - integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==, - } - engines: { node: '>= 6' } - - /yaml@2.3.1: - resolution: - { - integrity: sha512-2eHWfjaoXgTBC2jNM1LRef62VQa0umtvRiDSk6HSzW7RvS5YtkabJrwYLLEKWBc8a5U2PTSCs+dJjUTJdlHsWQ==, - } - engines: { node: '>= 14' } - dev: true - - /yargs-parser@20.2.9: - resolution: - { - integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==, - } - engines: { node: '>=10' } - - /yargs-parser@21.1.1: - resolution: - { - integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==, - } - engines: { node: '>=12' } - - /yargs@16.2.0: - resolution: - { - integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==, - } - engines: { node: '>=10' } + + y18n@4.0.3: {} + + y18n@5.0.8: {} + + yallist@2.1.2: {} + + yallist@3.1.1: {} + + yallist@4.0.0: {} + + yaml@1.10.2: {} + + yaml@2.8.1: {} + + yargs-parser@18.1.3: + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + + yargs-parser@20.2.9: {} + + yargs-parser@21.1.1: {} + + yargs@15.4.1: + dependencies: + cliui: 6.0.0 + decamelize: 1.2.0 + find-up: 4.1.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 4.2.3 + which-module: 2.0.1 + y18n: 4.0.3 + yargs-parser: 18.1.3 + + yargs@16.2.0: dependencies: cliui: 7.0.4 - escalade: 3.1.1 + escalade: 3.2.0 get-caller-file: 2.0.5 require-directory: 2.1.1 string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 20.2.9 + optional: true - /yargs@17.7.2: - resolution: - { - integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==, - } - engines: { node: '>=12' } + yargs@17.7.2: dependencies: cliui: 8.0.1 - escalade: 3.1.1 + escalade: 3.2.0 get-caller-file: 2.0.5 require-directory: 2.1.1 string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 21.1.1 - /yn@3.1.1: - resolution: - { - integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==, - } - engines: { node: '>=6' } - dev: true - - /yocto-queue@0.1.0: - resolution: - { - integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==, - } - engines: { node: '>=10' } - requiresBuild: true + yocto-queue@0.1.0: {} diff --git a/web/static/img/blog/end-to-end-encryption-to-sms-messages/encryption-key-android.png b/web/static/img/blog/end-to-end-encryption-to-sms-messages/encryption-key-android.png new file mode 100644 index 00000000..0916a951 Binary files /dev/null and b/web/static/img/blog/end-to-end-encryption-to-sms-messages/encryption-key-android.png differ diff --git a/web/static/img/blog/end-to-end-encryption-to-sms-messages/send-sms-message.png b/web/static/img/blog/end-to-end-encryption-to-sms-messages/send-sms-message.png new file mode 100644 index 00000000..18c70136 Binary files /dev/null and b/web/static/img/blog/end-to-end-encryption-to-sms-messages/send-sms-message.png differ diff --git a/web/static/img/blog/grant-send-and-read-sms-permissions-on-android/allow-restricted-settings.png b/web/static/img/blog/grant-send-and-read-sms-permissions-on-android/allow-restricted-settings.png new file mode 100644 index 00000000..ce20961d Binary files /dev/null and b/web/static/img/blog/grant-send-and-read-sms-permissions-on-android/allow-restricted-settings.png differ diff --git a/web/static/img/blog/grant-send-and-read-sms-permissions-on-android/allow.png b/web/static/img/blog/grant-send-and-read-sms-permissions-on-android/allow.png new file mode 100644 index 00000000..08c6904b Binary files /dev/null and b/web/static/img/blog/grant-send-and-read-sms-permissions-on-android/allow.png differ diff --git a/web/static/img/blog/grant-send-and-read-sms-permissions-on-android/app-info.png b/web/static/img/blog/grant-send-and-read-sms-permissions-on-android/app-info.png new file mode 100644 index 00000000..9eda0ba6 Binary files /dev/null and b/web/static/img/blog/grant-send-and-read-sms-permissions-on-android/app-info.png differ diff --git a/web/static/integrations.js b/web/static/integrations.js index 42f2908b..4fe4b5ae 100644 --- a/web/static/integrations.js +++ b/web/static/integrations.js @@ -13,11 +13,3 @@ // LemonSqueezy window.lemonSqueezyAffiliateConfig = { store: 'httpsms' } - -// substack -window.CustomSubstackWidget = { - substackUrl: 'httpsms.substack.com', - placeholder: 'example@gmail.com', - buttonText: 'Subscribe', - theme: 'green', -} diff --git a/web/static/robots.txt b/web/static/robots.txt new file mode 100644 index 00000000..1a68258b --- /dev/null +++ b/web/static/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Allow: / + +Sitemap: https://httpsms.com/sitemap.xml diff --git a/web/static/templates/httpsms-bulk.csv b/web/static/templates/httpsms-bulk.csv index 38891f63..66411bc7 100644 --- a/web/static/templates/httpsms-bulk.csv +++ b/web/static/templates/httpsms-bulk.csv @@ -1,3 +1,3 @@ -FromPhoneNumber,ToPhoneNumber,Content -+18005550199,+18005550100,This is a sample text message1 -+18005550199,+18005550100,This is a sample text message2 +FromPhoneNumber,ToPhoneNumber,Content,SendTime(optional) +18005550199,18005550100,This is a sample text message1, +18005550199,18005550100,This is a sample text message2,2023-11-11T02:10:01 diff --git a/web/static/templates/httpsms-bulk.xlsx b/web/static/templates/httpsms-bulk.xlsx index bb713087..ca23f441 100644 Binary files a/web/static/templates/httpsms-bulk.xlsx and b/web/static/templates/httpsms-bulk.xlsx differ diff --git a/web/store/README.md b/web/store/README.md deleted file mode 100644 index 1972d277..00000000 --- a/web/store/README.md +++ /dev/null @@ -1,10 +0,0 @@ -# STORE - -**This directory is not required, you can delete it if you don't want to use it.** - -This directory contains your Vuex Store files. -Vuex Store option is implemented in the Nuxt.js framework. - -Creating a file in this directory automatically activates the option in the framework. - -More information about the usage of this directory in [the documentation](https://nuxtjs.org/guide/vuex-store). diff --git a/web/store/index.ts b/web/store/index.ts index 621a6a34..afeeaebd 100644 --- a/web/store/index.ts +++ b/web/store/index.ts @@ -1,27 +1,34 @@ import { ActionContext } from 'vuex' import { AxiosError, AxiosResponse } from 'axios' import { MessageThread } from '~/models/message-thread' -import { Message } from '~/models/message' +import { Message, SearchMessagesRequest } from '~/models/message' import { Heartbeat } from '~/models/heartbeat' import axios, { setApiKey, setAuthHeader } from '~/plugins/axios' import { User } from '~/models/user' import { BillingUsage } from '~/models/billing' import { EntitiesDiscord, + EntitiesMessage, EntitiesPhone, + EntitiesPhoneAPIKey, EntitiesUser, EntitiesWebhook, RequestsDiscordStore, RequestsDiscordUpdate, RequestsUserNotificationUpdate, + RequestsUserPaymentInvoice, RequestsWebhookStore, RequestsWebhookUpdate, ResponsesDiscordResponse, ResponsesDiscordsResponse, + ResponsesMessagesResponse, ResponsesNoContent, ResponsesOkString, + ResponsesPhoneAPIKeyResponse, + ResponsesPhoneAPIKeysResponse, ResponsesUnprocessableEntity, ResponsesUserResponse, + ResponsesUserSubscriptionPaymentsResponse, ResponsesWebhookResponse, ResponsesWebhooksResponse, } from '~/models/api' @@ -270,6 +277,7 @@ export const mutations = { state.user = null state.threadId = null state.archivedThreads = false + state.pooling = false state.owner = null setApiKey('') }, @@ -303,8 +311,9 @@ export const actions = { }, }) - await context.dispatch('getHeartbeat') - context.commit('setThreads', response.data.data) + // eslint-disable-next-line no-console + context.dispatch('getHeartbeat').catch(console.error) + await context.commit('setThreads', response.data.data) }, async loadBillingUsage(context: ActionContext) { @@ -365,6 +374,7 @@ export const actions = { message_expiration_seconds: parseInt( phone.message_expiration_seconds.toString(), ), + missed_call_auto_reply: phone.missed_call_auto_reply, max_send_attempts: parseInt(phone.max_send_attempts.toString()), messages_per_minute: parseInt(phone.messages_per_minute.toString()), }) @@ -412,6 +422,222 @@ export const actions = { }) }, + storePhoneApiKey(context: ActionContext, name: string) { + return new Promise((resolve, reject) => { + axios + .post(`/v1/phone-api-keys`, { name }) + .then(async (response: AxiosResponse) => { + await context.dispatch('addNotification', { + message: + response.data.message ?? 'Phone API Key created successfully', + type: 'success', + }) + resolve(response.data) + }) + .catch(async (error: AxiosError) => { + await Promise.all([ + context.dispatch('addNotification', { + message: + error.response?.data?.message ?? + 'Errors while creating phone API key', + type: 'error', + }), + ]) + reject(error) + }) + }) + }, + + indexPhoneApiKeys(context: ActionContext) { + return new Promise>((resolve, reject) => { + axios + .get(`/v1/phone-api-keys`, { + params: { + limit: 100, + }, + }) + .then((response: AxiosResponse) => { + resolve(response.data.data) + }) + .catch(async (error: AxiosError) => { + await Promise.all([ + context.dispatch('addNotification', { + message: + (error.response?.data as any)?.message ?? + 'Error while fetching phone API keys', + type: 'error', + }), + ]) + reject(getErrorMessages(error)) + }) + }) + }, + + deletePhoneApiKey( + context: ActionContext, + phoneAPIKeyID: string, + ) { + return new Promise((resolve, reject) => { + axios + .delete(`/v1/phone-api-keys/${phoneAPIKeyID}`) + .then(async (response: AxiosResponse) => { + await context.dispatch('addNotification', { + message: + response.data.message ?? + 'The phone API key has been deleted successfully', + type: 'success', + }) + resolve() + }) + .catch(async (error: AxiosError) => { + await Promise.all([ + context.dispatch('addNotification', { + message: + (error.response?.data as any)?.message ?? + 'Error while deleting phone API key', + type: 'error', + }), + ]) + reject(getErrorMessages(error)) + }) + }) + }, + + deletePhoneFromPhoneApiKey( + context: ActionContext, + payload: { phoneApiKeyId: string; phoneId: string }, + ) { + return new Promise((resolve, reject) => { + axios + .delete( + `/v1/phone-api-keys/${payload.phoneApiKeyId}/phones/${payload.phoneId}`, + ) + .then(async (response: AxiosResponse) => { + await context.dispatch('addNotification', { + message: + response.data.message ?? + 'The phone has been removed from the phone API key successfully', + type: 'success', + }) + resolve() + }) + .catch(async (error: AxiosError) => { + await Promise.all([ + context.dispatch('addNotification', { + message: + (error.response?.data as any)?.message ?? + 'Error while deleting phone API key', + type: 'error', + }), + ]) + reject(getErrorMessages(error)) + }) + }) + }, + + indexSubscriptionPayments(context: ActionContext) { + return new Promise( + (resolve, reject) => { + axios + .get( + `/v1/users/subscription/payments`, + { + params: { + limit: 100, + }, + }, + ) + .then( + ( + response: AxiosResponse, + ) => { + resolve(response.data) + }, + ) + .catch(async (error: AxiosError) => { + await Promise.all([ + context.dispatch('addNotification', { + message: + (error.response?.data as any)?.message ?? + 'Error while fetching subscription payments.', + type: 'error', + }), + ]) + reject(getErrorMessages(error)) + }) + }, + ) + }, + + generateSubscriptionPaymentInvoice( + context: ActionContext, + payload: { + subscriptionInvoiceId: string + request: RequestsUserPaymentInvoice + }, + ) { + return new Promise((resolve, reject) => { + axios + .post( + `/v1/users/subscription/invoices/${payload.subscriptionInvoiceId}`, + payload.request, + { + responseType: 'blob', + }, + ) + .then(async (response: AxiosResponse) => { + // Create a Blob from the response data + const pdfBlob = new Blob([response.data], { + type: response.headers['content-type'], + }) + + // Create a temporary URL for the Blob + const url = window.URL.createObjectURL(pdfBlob) + + // Create a temporary element to trigger the download + const tempLink = document.createElement('a') + tempLink.href = url + tempLink.setAttribute( + 'download', + response.headers['content-disposition'] + ?.split('filename=')[1] + .replaceAll('"', '') || 'Invoice.pdf', + ) // Set the desired filename for the downloaded file + + // Append the element to the body and click it to trigger the download + document.body.appendChild(tempLink) + tempLink.click() + + // Clean up the temporary elements and URL + document.body.removeChild(tempLink) + window.URL.revokeObjectURL(url) + + await context.dispatch('addNotification', { + message: + response.data.message ?? + 'Your invoice has been generated successfully', + type: 'success', + }) + resolve() + }) + .catch(async (error: AxiosError) => { + const text = await (error.response as any).data.text() + if (error.response) { + error.response.data = JSON.parse(text) + } + await Promise.all([ + context.dispatch('addNotification', { + message: + (error.response?.data as any)?.message ?? + 'Error while generating your invoice', + type: 'error', + }), + ]) + reject(getErrorMessages(error)) + }) + }) + }, + async handleAxiosError( context: ActionContext, error: AxiosError, @@ -485,6 +711,52 @@ export const actions = { await Promise.all([context.dispatch('loadThreads')]) }, + deleteMessage(context: ActionContext, messageId: string) { + return new Promise((resolve, reject) => { + axios + .delete(`/v1/messages/${messageId}`) + .then(async () => { + await context.dispatch('addNotification', { + message: 'The message has been deleted successfully', + type: 'success', + }) + resolve() + }) + .catch(async (error: AxiosError) => { + await Promise.all([ + context.dispatch('addNotification', { + message: + (error.response?.data as any)?.message ?? + 'Error while deleting message', + type: 'error', + }), + ]) + reject(getErrorMessages(error)) + }) + }) + }, + + searchMessages( + _: ActionContext, + payload: SearchMessagesRequest, + ) { + const token = payload.token + delete payload.token + return new Promise((resolve, reject) => { + axios + .get(`/v1/messages/search`, { + params: payload, + headers: { token }, + }) + .then((response: AxiosResponse) => { + resolve(response.data.data) + }) + .catch((error: AxiosError) => { + reject(error) + }) + }) + }, + setThreadId(context: ActionContext, threadId: string | null) { context.commit('setThreadId', threadId) }, @@ -511,7 +783,7 @@ export const actions = { params: { contact: context.getters.getThread.contact, owner: context.getters.getThread.owner, - limit: 100, + limit: 50, }, }) .then((response: AxiosResponse) => { @@ -614,6 +886,45 @@ export const actions = { context.commit('setUser', response.data.data) }, + deleteUserAccount(context: ActionContext) { + return new Promise((resolve, reject) => { + axios + .delete(`/v1/users/me`) + .then((response: AxiosResponse) => { + resolve(response.data.message) + }) + .catch(async (error: AxiosError) => { + await Promise.all([ + context.dispatch('addNotification', { + message: + (error.response?.data as any)?.message ?? + 'Error while deleting your user account', + type: 'error', + }), + ]) + reject(getErrorMessages(error)) + }) + }) + }, + + updateTimezone( + context: ActionContext, + payload: string, + ): Promise { + return new Promise((resolve, reject) => { + axios + .put(`/v1/users/me`, { + timezone: payload ?? context.getters.getUser.timezone, + }) + .then((response: AxiosResponse) => { + resolve(response.data.data) + }) + .catch((error: AxiosError) => { + reject(getErrorMessages(error)) + }) + }) + }, + async updateThread( context: ActionContext, payload: { threadId: string; isArchived: boolean }, @@ -625,6 +936,32 @@ export const actions = { await context.dispatch('loadThreads') }, + deleteThread(context: ActionContext, threadId: string) { + return new Promise((resolve, reject) => { + axios + .delete(`/v1/message-threads/${threadId}`) + .then(async () => { + context.commit('setThreadId', null) + await context.dispatch('addNotification', { + message: 'The message thread has been deleted successfully', + type: 'success', + }) + resolve() + }) + .catch(async (error: AxiosError) => { + await Promise.all([ + context.dispatch('addNotification', { + message: + (error.response?.data as any)?.message ?? + 'Error while deleting message thread', + type: 'error', + }), + ]) + reject(getErrorMessages(error)) + }) + }) + }, + getSubscriptionUpdateLink(context: ActionContext) { return new Promise((resolve, reject) => { axios @@ -816,6 +1153,33 @@ export const actions = { }) }, + rotateApiKey(context: ActionContext, payload: string) { + return new Promise((resolve, reject) => { + axios + .delete(`/v1/users/${payload}/api-keys`) + .then((response: AxiosResponse) => { + context.commit('setUser', response.data.data) + setApiKey(response.data.data.api_key) + context.dispatch('addNotification', { + message: 'API Key rotated successfully', + type: 'success', + }) + resolve(response.data.data) + }) + .catch(async (error: AxiosError) => { + await Promise.all([ + context.dispatch('addNotification', { + message: + (error.response?.data as any)?.message ?? + 'Error while rotating your API key', + type: 'error', + }), + ]) + reject(getErrorMessages(error)) + }) + }) + }, + updateWebhook( context: ActionContext, payload: RequestsWebhookUpdate & { id: string },