commit 18ff9fdd9babd75722ee6046fe95b61b409f039a
Author: kk034kk034
Date: Thu May 1 13:25:28 2025 +0800
[init] Open source from splitpro.
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..b2ee101
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,57 @@
+# .env
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# database
+/prisma/db.sqlite
+/prisma/db.sqlite-journal
+
+# next.js
+/.next/
+/out/
+next-env.d.ts
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.pnpm-debug.log*
+
+# local env files
+# do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables
+# .env
+.env
+.env.example
+.env*.local
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+
+**/public/workbox-*.js
+**/public/workbox-*.js.map
+**/public/sw.js
+**/public/sw.js.map
+**/public/worker-*.js
+src/server/random.code-workspace
+
+prisma/seed.ts
+
+creds
+package-lock.json
diff --git a/.env.example b/.env.example
new file mode 100644
index 0000000..88b4678
--- /dev/null
+++ b/.env.example
@@ -0,0 +1,72 @@
+# When adding additional environment variables, the schema in "/src/env.js"
+# should be updated accordingly.
+
+#********* REQUIRED ENV VARS *********
+
+# Prisma
+# DataBase ENV VARS
+# You could give a DB URL or give the username, password, host, port individually
+# https://www.prisma.io/docs/reference/database-reference/connection-urls#env
+DATABASE_URL="postgresql://splitpro:password@localhost:54321/splitpro"
+
+# These variables are also used by docker compose in compose.yml to name the container
+# and initialise postgres with default username, password
+# POSTGRES_USER="postgres"
+# POSTGRES_PASSWORD="strong-password"
+# POSTGRES_DB="splitpro"
+# DATABASE_URL="postgresql://postgres:strong-password@splitpro-db-prod:5432/splitpro"
+
+# Next Auth
+# You can generate a new secret on the command line with:
+# openssl rand -base64 32
+# https://next-auth.js.org/configuration/options#secret
+NEXTAUTH_SECRET="secret"
+NEXTAUTH_URL="http://localhost:3000"
+
+# If provided, server-side calls will use this instead of NEXTAUTH_URL. Useful in environments when the server doesn't have access to the canonical URL of your site.
+# NEXTAUTH_URL_INTERNAL="http://localhost:3000"
+
+
+# Enable sending invites
+ENABLE_SENDING_INVITES=false
+#********* END OF REQUIRED ENV VARS *********
+
+
+#********* OPTIONAL ENV VARS *********
+# SMTP options
+FROM_EMAIL=
+EMAIL_SERVER_HOST=
+EMAIL_SERVER_PORT=
+EMAIL_SERVER_USER=
+EMAIL_SERVER_PASSWORD=
+
+# Google Provider : https://next-auth.js.org/providers/google
+GOOGLE_CLIENT_ID=
+GOOGLE_CLIENT_SECRET=
+
+# Authentic Providder : https://next-auth.js.org/providers/authentik
+# Issuer: should include the slug without a trailing slash – e.g., https://my-authentik-domain.com/application/o/splitpro
+AUTHENTIK_ID=
+AUTHENTIK_SECRET=
+AUTHENTIK_ISSUER=
+
+# Storage: any S3 compatible storage will work, for self hosting can use minio
+# If you're using minio for dev, you can generate access keys from the console http://localhost:9001/access-keys/new-account
+# R2_ACCESS_KEY="access-key"
+# R2_SECRET_KEY="secret-key"
+# R2_BUCKET="splitpro"
+# R2_URL="http://localhost:9002"
+# R2_PUBLIC_URL="http://localhost:9002/splitpro"
+
+# Push notification, Web Push: https://www.npmjs.com/package/web-push
+# generate web push keys using this command: web-push generate-vapid-keys --json
+WEB_PUSH_PRIVATE_KEY=
+WEB_PUSH_PUBLIC_KEY=
+WEB_PUSH_EMAIL=
+
+# Email options
+FEEDBACK_EMAIL=
+
+# Discord webhook for error notifications
+DISCORD_WEBHOOK_URL=
+#********* END OF OPTIONAL ENV VARS *********
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
new file mode 100644
index 0000000..0082a1f
--- /dev/null
+++ b/.eslintrc.cjs
@@ -0,0 +1,37 @@
+/** @type {import("eslint").Linter.Config} */
+const config = {
+ parser: "@typescript-eslint/parser",
+ parserOptions: {
+ project: true,
+ },
+ plugins: ["@typescript-eslint"],
+ extends: [
+ "next/core-web-vitals",
+ "plugin:@typescript-eslint/recommended-type-checked",
+ "plugin:@typescript-eslint/stylistic-type-checked",
+ ],
+ rules: {
+ // These opinionated rules are enabled in stylistic-type-checked above.
+ // Feel free to reconfigure them to your own preference.
+ "@typescript-eslint/array-type": "off",
+ "@typescript-eslint/consistent-type-definitions": "off",
+
+ "@typescript-eslint/consistent-type-imports": [
+ "warn",
+ {
+ prefer: "type-imports",
+ fixStyle: "inline-type-imports",
+ },
+ ],
+ "@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_" }],
+ "@typescript-eslint/require-await": "off",
+ "@typescript-eslint/no-misused-promises": [
+ "error",
+ {
+ checksVoidReturn: { attributes: false },
+ },
+ ],
+ },
+};
+
+module.exports = config;
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..33ef4ac
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,52 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# database
+/prisma/db.sqlite
+/prisma/db.sqlite-journal
+
+# next.js
+/.next/
+/out/
+next-env.d.ts
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+.idea
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.pnpm-debug.log*
+
+# local env files
+# do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables
+.env
+.env*.local
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+
+**/public/workbox-*.js
+**/public/workbox-*.js.map
+**/public/sw.js
+**/public/sw.js.map
+**/public/worker-*.js
+src/server/random.code-workspace
+
+certificates
diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md
new file mode 100644
index 0000000..7171b27
--- /dev/null
+++ b/CODE_OF_CONDUCT.md
@@ -0,0 +1,126 @@
+# 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 e-mail 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
+koushik@ossapps.dev.
+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].
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..ba04a58
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,52 @@
+# Contributing to Splitpro
+
+If you plan to contribute to Splitpro, please take a moment to feel awesome ✨ People like you are what open source is about ♥. Any contributions, no matter how big or small, are highly appreciated.
+
+## Before getting started
+
+- Before jumping into a PR be sure to search [existing PRs](https://github.com/oss-apps/split-pro/pulls) or [issues](https://github.com/oss-apps/split-pro/issues) for an open or closed item that relates to your submission.
+- Select an issue from [here](https://github.com/oss-apps/split-pro/issues) or create a new one
+- Consider the results from the discussion on the issue
+
+## Taking issues
+
+Before taking an issue, ensure that:
+
+- The issue is clearly defined and understood
+- No one has been assigned to the issue
+- No one has expressed intention to work on it
+
+You can then:
+
+1. Comment on the issue with your intention to work on it
+2. Begin work on the issue
+
+Always feel free to ask questions or seek clarification on the issue.
+
+## Developing
+
+The development branch is main. All pull requests should be made against this branch. If you need help getting started, send an email to koushik@ossapps.dev.
+
+1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your
+ own GitHub account and then
+ [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device.
+2. Create a new branch:
+
+- Create a new branch (include the issue id and something readable):
+
+ ```sh
+ git checkout -b feat/doc-999-somefeature-that-rocks
+ ```
+
+3. See the [Developer Setup](https://github.com/oss-apps/split-pro?tab=readme-ov-file#developer-setup) for more setup details.
+
+## Building
+
+> **Note**
+> Please ensure you can make a full production build before pushing code or creating PRs.
+
+You can build the project with:
+
+```bash
+pnpm build
+```
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..ab05252
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,9 @@
+MIT License
+
+Copyright (c) 2024 OSS Apps
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..ed925d6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,120 @@
+
+
+
+
+
+
SplitPro
+ An open source alternative to Splitwise
+
+
+ To our App »
+
+
+
+
+
+## About
+
+SplitPro aims to provide an open-source way to share expenses with your friends.
+
+It's meant to be a complete replacement for Splitwise.
+
+It currently has most of the important features.
+
+- Add expenses with an individual or groups
+- Overall balances across the groups
+- Multiple currency support
+- Upload expense bills
+- PWA support
+- Split expense unequally (share, percentage, exact amounts, adjustments)
+- Push notification
+- Download your data
+- Import from splitwise
+
+**More features coming every day**
+
+---
+
+## Why
+
+Splitwise is one of the best apps to add expenses and bills.
+
+I understand that every app needs to make money, After all, lots of effort has been put into Splitwise. My main problem is how they implemented this.
+
+Monetising on pro features or ads is fine, but asking money for adding expenses (core feature) is frustrating.
+
+I was searching for other open-source alternatives (Let's be honest, any closed-source product might do the same and I don't have any reason to believe otherwise).
+
+I managed to find a good app [spliit.app](https://spliit.app/) by [Sebastien Castiel](https://scastiel.dev/) but it's not a complete replacement and didn't suit my workflow sadly. Check it out to see if it fits you.
+
+_That's when I decided to work on this_
+
+## Tech stack
+
+- [NextJS](https://nextjs.org/)
+- [Tailwind](https://tailwindcss.com/)
+- [tRPC](https://trpc.io/)
+- [ShadcnUI](https://ui.shadcn.com/)
+- [Prisma](https://www.prisma.io/)
+- [Postgres](https://www.postgresql.org/)
+- [NextAuth](https://next-auth.js.org/)
+
+## Getting started.
+
+### Prerequisites
+
+- Node.js (Version: >=18.x)
+- PostgreSQL
+- pnpm (recommended)
+
+## Docker
+
+We provide a Docker container for Splitpro, which is published on both DockerHub and GitHub Container Registry.
+
+DockerHub: [https://hub.docker.com/r/ossapps/splitpro](https://hub.docker.com/r/ossapps/splitpro)
+
+GitHub Container Registry: [https://ghcr.io/oss-apps/splitpro](https://ghcr.io/oss-apps/splitpro)
+
+You can pull the Docker image from either of these registries and run it with your preferred container hosting provider.
+
+Please note that you will need to provide environment variables for connecting to the database, redis, aws and so forth.
+
+For detailed instructions on how to configure and run the Docker container, please refer to the Docker [Docker README](./docker/README.md) in the docker directory.
+
+## Developer Setup
+
+### Install Dependencies
+
+```bash
+corepack enable
+```
+
+```bash
+pnpm i
+```
+
+### Setting up the environment
+
+- Copy the env.example file into .env
+- Setup google oauth required for auth https://next-auth.js.org/providers/google or Email provider by setting SMTP details
+- Login to minio console using `splitpro` user and password `password` and [create access keys](http://localhost:9001/access-keys/new-account) and the R2 related env variables
+
+### Run the app
+
+```bash
+pnpm d
+```
+
+## Sponsors
+
+We are grateful for the support of our sponsors.
+
+### Our Sponsors
+
+
+
+
+
+## Star History
+
+[](https://star-history.com/#oss-apps/split-pro&Date)
diff --git a/components.json b/components.json
new file mode 100644
index 0000000..40c9cc4
--- /dev/null
+++ b/components.json
@@ -0,0 +1,17 @@
+{
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "default",
+ "rsc": false,
+ "tsx": true,
+ "tailwind": {
+ "config": "tailwind.config.ts",
+ "css": "src/styles/globals.css",
+ "baseColor": "gray",
+ "cssVariables": true,
+ "prefix": ""
+ },
+ "aliases": {
+ "components": "~/components",
+ "utils": "~/lib/utils"
+ }
+}
\ No newline at end of file
diff --git a/docker/Dockerfile b/docker/Dockerfile
new file mode 100644
index 0000000..3596d46
--- /dev/null
+++ b/docker/Dockerfile
@@ -0,0 +1,51 @@
+FROM node:20.11.1-alpine AS base
+ENV SKIP_ENV_VALIDATION="true"
+ENV DOCKER_OUTPUT=1
+ENV NEXT_TELEMETRY_DISABLED 1
+
+RUN apk add --no-cache libc6-compat
+RUN apk update
+
+WORKDIR /app
+RUN npm i -g pnpm@8.9
+RUN ls
+COPY package.json pnpm-lock.yaml ./
+
+RUN pnpm install
+
+COPY . .
+RUN pnpm generate
+RUN pnpm build
+
+FROM node:20-alpine3.19 as release
+WORKDIR /app
+RUN npm i -g pnpm@8.9
+
+RUN apk add --no-cache libc6-compat
+RUN apk update
+
+
+COPY --from=base /app/next.config.js .
+COPY --from=base /app/package.json .
+COPY --from=base /app/pnpm-lock.yaml .
+
+COPY --from=base /app/.next/standalone ./
+COPY --from=base /app/.next/static ./.next/static
+COPY --from=base /app/public ./public
+
+COPY --from=base /app/prisma/schema.prisma ./prisma/schema.prisma
+COPY --from=base /app/prisma/migrations ./prisma/migrations
+COPY --from=base /app/node_modules/prisma ./node_modules/prisma
+COPY --from=base /app/node_modules/@prisma ./node_modules/@prisma
+COPY --from=base /app/node_modules/sharp ./node_modules/sharp
+
+# Symlink the prisma binary
+RUN mkdir node_modules/.bin
+RUN ln -s /app/node_modules/prisma/build/index.js ./node_modules/.bin/prisma
+
+# set this so it throws error where starting server
+ENV SKIP_ENV_VALIDATION="false"
+
+COPY ./docker/start.sh ./start.sh
+
+CMD ["sh", "start.sh"]
diff --git a/docker/README.md b/docker/README.md
new file mode 100644
index 0000000..e89a19a
--- /dev/null
+++ b/docker/README.md
@@ -0,0 +1,65 @@
+# Docker Setup for Splitpro
+
+The following guide will walk you through setting up Splitpro using Docker. You can choose between a production setup using Docker Compose or a standalone container.
+
+## Prerequisites
+
+Before you begin, ensure that you have the following installed:
+
+- Docker
+- Docker Compose (if using the Docker Compose setup)
+
+## Option 1: Production Docker Compose Setup
+
+This setup includes PostgreSQL and the Splitpro application.
+
+1. Download the Docker Compose file from the Splitpro repository: [compose.yml](https://github.com/oss-apps/split-pro/blob/main/docker/prod/compose.yml)
+2. Navigate to the directory containing the `compose.yml` file.
+3. Create a `.env` file in the same directory. Copy the contents of `.env.example`
+4. Run the following command to start the containers:
+
+```
+docker-compose --env-file ./.env up -d
+```
+
+This will start the PostgreSQL database and the Splitpro application containers.
+
+5. Access the Splitpro application by visiting `http://localhost:3000` in your web browser.
+
+## Option 2: Standalone Docker Container
+
+If you prefer to host the Splitpro application on your container provider of choice, you can use the pre-built Docker image from DockerHub or GitHub's Package Registry.
+
+1. Pull the Splitpro Docker image:
+
+```
+docker pull ossapps/splitpro
+```
+
+Or, if using GitHub's Package Registry:
+
+```
+docker pull ghcr.io/oss-apps/splitpro
+```
+
+2. Run the Docker container, providing the necessary environment variables for your database and SMTP host:
+
+```
+docker run -d \
+ -p ${PORT:-3000}:${PORT:-3000} \
+ -e PORT=${PORT:-3000} \
+ -e DATABASE_URL=${DATABASE_URL:?err} \
+ -e NEXTAUTH_URL=${NEXTAUTH_URL:?err} \
+ -e NEXTAUTH_SECRET=${NEXTAUTH_SECRET:?err} \
+ -e GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID:?err} \
+ -e GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET:?err}
+ ossapps/splitpro
+```
+
+Replace the placeholders with your actual database and aws details.
+
+1. Access the Splitpro application by visiting the URL you provided in the `NEXTAUTH_URL` environment variable in your web browser.
+
+## Success
+
+You have now successfully set up Splitpro using Docker. If you encounter any issues or have further questions, please seek assistance from the community.
diff --git a/docker/build.sh b/docker/build.sh
new file mode 100644
index 0000000..c130c47
--- /dev/null
+++ b/docker/build.sh
@@ -0,0 +1,26 @@
+#!/usr/bin/env bash
+
+command -v docker >/dev/null 2>&1 || {
+ echo "Docker is not running. Please start Docker and try again."
+ exit 1
+}
+
+SCRIPT_DIR="$(readlink -f "$(dirname "$0")")"
+MONOREPO_ROOT="$(readlink -f "$SCRIPT_DIR/../")"
+
+APP_VERSION="$(git name-rev --tags --name-only $(git rev-parse HEAD) | head -n 1 | sed 's/\^0//')"
+GIT_SHA="$(git rev-parse HEAD)"
+
+echo "Building docker image for monorepo at $MONOREPO_ROOT"
+echo "App version: $APP_VERSION"
+echo "Git SHA: $GIT_SHA"
+
+docker build -f "$SCRIPT_DIR/Dockerfile" \
+ --progress=plain \
+ -t "ossapps/splitpro:latest" \
+ -t "ossapps/splitpro:$GIT_SHA" \
+ -t "ossapps/splitpro:$APP_VERSION" \
+ -t "ghcr.io/oss-apps/splitpro:latest" \
+ -t "ghcr.io/oss-apps/splitpro:$GIT_SHA" \
+ -t "ghcr.io/oss-apps/splitpro:$APP_VERSION" \
+ "$MONOREPO_ROOT"
\ No newline at end of file
diff --git a/docker/dev/compose.yml b/docker/dev/compose.yml
new file mode 100644
index 0000000..5b1e317
--- /dev/null
+++ b/docker/dev/compose.yml
@@ -0,0 +1,33 @@
+name: split-pro-dev
+
+services:
+ postgres:
+ image: postgres:16
+ container_name: splitpro-db-dev
+ restart: always
+ environment:
+ - POSTGRES_USER=splitpro
+ - POSTGRES_PASSWORD=password
+ - POSTGRES_DB=splitpro
+ volumes:
+ - database:/var/lib/postgresql/data
+ ports:
+ - '54321:5432'
+
+ minio:
+ image: minio/minio
+ container_name: splitpro-storage-dev
+ ports:
+ - 9002:9002
+ - 9001:9001
+ volumes:
+ - minio:/data
+ environment:
+ MINIO_ROOT_USER: splitpro
+ MINIO_ROOT_PASSWORD: password
+ entrypoint: sh
+ command: -c 'mkdir -p /data/splitpro && minio server /data --console-address ":9001" --address ":9002"'
+
+volumes:
+ database:
+ minio:
diff --git a/docker/prod/compose.yml b/docker/prod/compose.yml
new file mode 100644
index 0000000..3f0b94c
--- /dev/null
+++ b/docker/prod/compose.yml
@@ -0,0 +1,60 @@
+name: split-pro-prod
+
+services:
+ postgres:
+ image: postgres:16
+ container_name: splitpro-db-prod
+ restart: always
+ environment:
+ - POSTGRES_USER=${POSTGRES_USER:?err}
+ - POSTGRES_PASSWORD=${POSTGRES_PASSWORD:?err}
+ - POSTGRES_DB=${POSTGRES_DB:?err}
+ healthcheck:
+ test: ['CMD-SHELL', 'pg_isready -U ${POSTGRES_USER}']
+ interval: 10s
+ timeout: 5s
+ retries: 5
+ # ports:
+ # - "5432:5432"
+ volumes:
+ - database:/var/lib/postgresql/data
+
+ splitpro:
+ image: ossapps/splitpro:latest
+ container_name: splitpro
+ restart: always
+ ports:
+ - ${PORT:-3000}:${PORT:-3000}
+ environment:
+ - PORT=${PORT:-3000}
+ - DATABASE_URL=${DATABASE_URL:?err}
+ - NEXTAUTH_URL=${NEXTAUTH_URL:?err}
+ - NEXTAUTH_SECRET=${NEXTAUTH_SECRET:?err}
+ - ENABLE_SENDING_INVITES=${ENABLE_SENDING_INVITES:?err}
+ - FROM_EMAIL=${FROM_EMAIL}
+ - EMAIL_SERVER_HOST=${EMAIL_SERVER_HOST}
+ - EMAIL_SERVER_PORT=${EMAIL_SERVER_PORT}
+ - EMAIL_SERVER_USER=${EMAIL_SERVER_USER}
+ - EMAIL_SERVER_PASSWORD=${EMAIL_SERVER_PASSWORD}
+ - GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID}
+ - GOOGLE_CLIENT_SECRET=${GOOGLE_CLIENT_SECRET}
+ - AUTHENTIK_ID=${AUTHENTIK_ID}
+ - AUTHENTIK_SECRET=${AUTHENTIK_SECRET}
+ - AUTHENTIK_ISSUER=${AUTHENTIK_ISSUER}
+ - R2_ACCESS_KEY=${R2_ACCESS_KEY}
+ - R2_SECRET_KEY=${R2_SECRET_KEY}
+ - R2_BUCKET=${R2_BUCKET}
+ - R2_URL=${R2_URL}
+ - R2_PUBLIC_URL=${R2_PUBLIC_URL}
+ - WEB_PUSH_PRIVATE_KEY=${WEB_PUSH_PRIVATE_KEY}
+ - WEB_PUSH_PUBLIC_KEY=${WEB_PUSH_PUBLIC_KEY}
+ - WEB_PUSH_EMAIL=${WEB_PUSH_EMAIL}
+ - FEEDBACK_EMAIL=${FEEDBACK_EMAIL}
+ - DISCORD_WEBHOOK_URL=${DISCORD_WEBHOOK_URL}
+ depends_on:
+ postgres:
+ condition: service_healthy
+
+volumes:
+ database:
+
diff --git a/docker/start.sh b/docker/start.sh
new file mode 100644
index 0000000..541f790
--- /dev/null
+++ b/docker/start.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+set -x
+
+echo "Deploying prisma migrations"
+
+pnpx prisma migrate deploy --schema ./prisma/schema.prisma
+
+echo "Starting web server"
+
+node server.js
+
diff --git a/next.config.js b/next.config.js
new file mode 100644
index 0000000..8418d4d
--- /dev/null
+++ b/next.config.js
@@ -0,0 +1,58 @@
+/* eslint-disable @typescript-eslint/ban-ts-comment */
+/* eslint-disable @typescript-eslint/no-unsafe-call */
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
+/**
+ * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially useful
+ * for Docker builds.
+ */
+await import('./src/env.js');
+
+/** @type {import("next").NextConfig} */
+
+import pwa from 'next-pwa';
+// @ts-ignore
+import nextra from 'nextra';
+
+// eslint-disable-next-line @typescript-eslint/no-unsafe-call
+const withPwa = pwa({
+ dest: 'public',
+ // disable: process.env.NODE_ENV === 'development',
+});
+
+const config = {
+ reactStrictMode: true,
+ output: process.env.DOCKER_OUTPUT ? 'standalone' : undefined,
+ experimental: {
+ instrumentationHook: true,
+ },
+ /**
+ * If you are using `appDir` then you must comment the below `i18n` config out.
+ *
+ * @see https://github.com/vercel/next.js/issues/41980
+ */
+ i18n: {
+ locales: ['en'],
+ defaultLocale: 'en',
+ },
+ transpilePackages: ['geist'],
+ images: {
+ remotePatterns: [
+ {
+ protocol: 'https',
+ hostname: '**',
+ },
+ {
+ protocol: 'http',
+ hostname: '**',
+ },
+ ],
+ },
+};
+
+const withNextra = nextra({
+ theme: 'nextra-theme-blog',
+ themeConfig: './theme.config.jsx',
+});
+
+// @ts-ignore
+export default withNextra(withPwa(config));
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..09b4c29
--- /dev/null
+++ b/package.json
@@ -0,0 +1,108 @@
+{
+ "name": "split",
+ "version": "0.1.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "build": "next build",
+ "just-build": "next build",
+ "db:push": "prisma db push",
+ "db:studio": "prisma studio",
+ "db:dev": "prisma migrate dev",
+ "db:seed": "prisma db seed",
+ "prisma:prod": "prisma migrate deploy",
+ "dev": "next dev",
+ "postinstall": "prisma generate",
+ "generate": "prisma generate",
+ "lint": "next lint",
+ "start": "sleep 3 && pnpm prisma:prod && next start",
+ "start-with-latest-migrations": "prisma migrate deploy && next start",
+ "d": "pnpm dx && pnpm dev",
+ "dx": "pnpm i && pnpm dx:up && pnpm db:dev",
+ "dx:up": "docker compose -f docker/dev/compose.yml up -d",
+ "dx:down": "docker compose -f docker/dev/compose.yml down"
+ },
+ "dependencies": {
+ "@aws-sdk/client-s3": "^3.515.0",
+ "@aws-sdk/s3-request-presigner": "^3.515.0",
+ "@heroicons/react": "^2.1.1",
+ "@hookform/resolvers": "^3.3.4",
+ "@next-auth/prisma-adapter": "^1.0.7",
+ "@prisma/client": "^5.9.1",
+ "@radix-ui/react-alert-dialog": "^1.0.5",
+ "@radix-ui/react-avatar": "^1.0.4",
+ "@radix-ui/react-checkbox": "^1.0.4",
+ "@radix-ui/react-dialog": "^1.0.5",
+ "@radix-ui/react-label": "^2.0.2",
+ "@radix-ui/react-popover": "^1.0.7",
+ "@radix-ui/react-separator": "^1.0.3",
+ "@radix-ui/react-slot": "^1.0.2",
+ "@radix-ui/react-tabs": "^1.0.4",
+ "@t3-oss/env-nextjs": "^0.11.1",
+ "@tanstack/react-query": "^4.36.1",
+ "@trpc/client": "^10.43.6",
+ "@trpc/next": "^10.43.6",
+ "@trpc/react-query": "^10.43.6",
+ "@trpc/server": "^10.43.6",
+ "babel-loader": "^9.1.3",
+ "boring-avatars": "^1.10.1",
+ "class-variance-authority": "^0.7.0",
+ "clsx": "^2.1.0",
+ "cmdk": "^0.2.0",
+ "date-fns": "^3.3.1",
+ "framer-motion": "^11.0.3",
+ "geist": "^1.2.1",
+ "input-otp": "^1.2.3",
+ "lucide-react": "^0.312.0",
+ "nanoid": "^5.0.6",
+ "next": "^14.0.4",
+ "next-auth": "^4.24.5",
+ "next-pwa": "^5.6.0",
+ "next-themes": "^0.2.1",
+ "nextra": "^2.13.4",
+ "nextra-theme-blog": "^2.13.4",
+ "nodemailer": "^6.9.8",
+ "react": "18.2.0",
+ "react-day-picker": "^8.10.0",
+ "react-dom": "18.2.0",
+ "react-hook-form": "^7.50.1",
+ "resend": "^3.2.0",
+ "sharp": "0.32.6",
+ "sonner": "^1.4.0",
+ "superjson": "^2.2.1",
+ "tailwind-merge": "^2.2.0",
+ "tailwindcss-animate": "^1.0.7",
+ "vaul": "^0.8.9",
+ "web-push": "^3.6.7",
+ "zod": "^3.22.4",
+ "zustand": "^4.5.0"
+ },
+ "devDependencies": {
+ "@types/eslint": "^8.44.7",
+ "@types/next-pwa": "^5.6.9",
+ "@types/node": "^18.17.0",
+ "@types/nodemailer": "^6.4.15",
+ "@types/react": "^18.2.37",
+ "@types/react-dom": "^18.2.15",
+ "@types/web-push": "^3.6.3",
+ "@typescript-eslint/eslint-plugin": "^6.11.0",
+ "@typescript-eslint/parser": "^6.11.0",
+ "autoprefixer": "^10.4.14",
+ "eslint": "^8.54.0",
+ "eslint-config-next": "^14.0.4",
+ "postcss": "^8.4.31",
+ "prettier": "^3.1.0",
+ "prettier-plugin-tailwindcss": "^0.5.7",
+ "prisma": "^5.9.1",
+ "tailwindcss": "^3.3.5",
+ "tsx": "^4.7.1",
+ "typescript": "^5.1.6"
+ },
+ "ct3aMetadata": {
+ "initVersion": "7.25.2"
+ },
+ "prisma": {
+ "seed": "tsx prisma/seed.ts"
+ },
+ "packageManager": "pnpm@8.9.2"
+}
\ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 0000000..4b58d5e
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,11198 @@
+lockfileVersion: '6.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+dependencies:
+ '@aws-sdk/client-s3':
+ specifier: ^3.515.0
+ version: 3.515.0
+ '@aws-sdk/s3-request-presigner':
+ specifier: ^3.515.0
+ version: 3.515.0
+ '@heroicons/react':
+ specifier: ^2.1.1
+ version: 2.1.1(react@18.2.0)
+ '@hookform/resolvers':
+ specifier: ^3.3.4
+ version: 3.3.4(react-hook-form@7.50.1)
+ '@next-auth/prisma-adapter':
+ specifier: ^1.0.7
+ version: 1.0.7(@prisma/client@5.9.1)(next-auth@4.24.5)
+ '@prisma/client':
+ specifier: ^5.9.1
+ version: 5.9.1(prisma@5.9.1)
+ '@radix-ui/react-alert-dialog':
+ specifier: ^1.0.5
+ version: 1.0.5(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-avatar':
+ specifier: ^1.0.4
+ version: 1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-checkbox':
+ specifier: ^1.0.4
+ version: 1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-dialog':
+ specifier: ^1.0.5
+ version: 1.0.5(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-label':
+ specifier: ^2.0.2
+ version: 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-popover':
+ specifier: ^1.0.7
+ version: 1.0.7(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-separator':
+ specifier: ^1.0.3
+ version: 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-slot':
+ specifier: ^1.0.2
+ version: 1.0.2(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-tabs':
+ specifier: ^1.0.4
+ version: 1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@t3-oss/env-nextjs':
+ specifier: ^0.11.1
+ version: 0.11.1(typescript@5.3.3)(zod@3.22.4)
+ '@tanstack/react-query':
+ specifier: ^4.36.1
+ version: 4.36.1(react-dom@18.2.0)(react@18.2.0)
+ '@trpc/client':
+ specifier: ^10.43.6
+ version: 10.45.0(@trpc/server@10.45.0)
+ '@trpc/next':
+ specifier: ^10.43.6
+ version: 10.45.0(@tanstack/react-query@4.36.1)(@trpc/client@10.45.0)(@trpc/react-query@10.45.0)(@trpc/server@10.45.0)(next@14.1.0)(react-dom@18.2.0)(react@18.2.0)
+ '@trpc/react-query':
+ specifier: ^10.43.6
+ version: 10.45.0(@tanstack/react-query@4.36.1)(@trpc/client@10.45.0)(@trpc/server@10.45.0)(react-dom@18.2.0)(react@18.2.0)
+ '@trpc/server':
+ specifier: ^10.43.6
+ version: 10.45.0
+ babel-loader:
+ specifier: ^9.1.3
+ version: 9.1.3(@babel/core@7.23.9)(webpack@5.90.1)
+ boring-avatars:
+ specifier: ^1.10.1
+ version: 1.10.1
+ class-variance-authority:
+ specifier: ^0.7.0
+ version: 0.7.0
+ clsx:
+ specifier: ^2.1.0
+ version: 2.1.0
+ cmdk:
+ specifier: ^0.2.0
+ version: 0.2.0(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ date-fns:
+ specifier: ^3.3.1
+ version: 3.3.1
+ framer-motion:
+ specifier: ^11.0.3
+ version: 11.0.3(react-dom@18.2.0)(react@18.2.0)
+ geist:
+ specifier: ^1.2.1
+ version: 1.2.1(next@14.1.0)
+ input-otp:
+ specifier: ^1.2.3
+ version: 1.2.3(react-dom@18.2.0)(react@18.2.0)
+ lucide-react:
+ specifier: ^0.312.0
+ version: 0.312.0(react@18.2.0)
+ nanoid:
+ specifier: ^5.0.6
+ version: 5.0.6
+ next:
+ specifier: ^14.0.4
+ version: 14.1.0(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0)
+ next-auth:
+ specifier: ^4.24.5
+ version: 4.24.5(next@14.1.0)(nodemailer@6.9.8)(react-dom@18.2.0)(react@18.2.0)
+ next-pwa:
+ specifier: ^5.6.0
+ version: 5.6.0(@babel/core@7.23.9)(next@14.1.0)(webpack@5.90.1)
+ next-themes:
+ specifier: ^0.2.1
+ version: 0.2.1(next@14.1.0)(react-dom@18.2.0)(react@18.2.0)
+ nextra:
+ specifier: ^2.13.4
+ version: 2.13.4(next@14.1.0)(react-dom@18.2.0)(react@18.2.0)
+ nextra-theme-blog:
+ specifier: ^2.13.4
+ version: 2.13.4(next@14.1.0)(nextra@2.13.4)(react-dom@18.2.0)(react@18.2.0)
+ nodemailer:
+ specifier: ^6.9.8
+ version: 6.9.8
+ react:
+ specifier: 18.2.0
+ version: 18.2.0
+ react-day-picker:
+ specifier: ^8.10.0
+ version: 8.10.0(date-fns@3.3.1)(react@18.2.0)
+ react-dom:
+ specifier: 18.2.0
+ version: 18.2.0(react@18.2.0)
+ react-hook-form:
+ specifier: ^7.50.1
+ version: 7.50.1(react@18.2.0)
+ resend:
+ specifier: ^3.2.0
+ version: 3.2.0
+ sharp:
+ specifier: 0.32.6
+ version: 0.32.6
+ sonner:
+ specifier: ^1.4.0
+ version: 1.4.0(react-dom@18.2.0)(react@18.2.0)
+ superjson:
+ specifier: ^2.2.1
+ version: 2.2.1
+ tailwind-merge:
+ specifier: ^2.2.0
+ version: 2.2.0
+ tailwindcss-animate:
+ specifier: ^1.0.7
+ version: 1.0.7(tailwindcss@3.4.1)
+ vaul:
+ specifier: ^0.8.9
+ version: 0.8.9(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ web-push:
+ specifier: ^3.6.7
+ version: 3.6.7
+ zod:
+ specifier: ^3.22.4
+ version: 3.22.4
+ zustand:
+ specifier: ^4.5.0
+ version: 4.5.0(@types/react@18.2.48)(react@18.2.0)
+
+devDependencies:
+ '@types/eslint':
+ specifier: ^8.44.7
+ version: 8.56.2
+ '@types/next-pwa':
+ specifier: ^5.6.9
+ version: 5.6.9(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0)
+ '@types/node':
+ specifier: ^18.17.0
+ version: 18.19.8
+ '@types/nodemailer':
+ specifier: ^6.4.15
+ version: 6.4.15
+ '@types/react':
+ specifier: ^18.2.37
+ version: 18.2.48
+ '@types/react-dom':
+ specifier: ^18.2.15
+ version: 18.2.18
+ '@types/web-push':
+ specifier: ^3.6.3
+ version: 3.6.3
+ '@typescript-eslint/eslint-plugin':
+ specifier: ^6.11.0
+ version: 6.19.0(@typescript-eslint/parser@6.19.0)(eslint@8.56.0)(typescript@5.3.3)
+ '@typescript-eslint/parser':
+ specifier: ^6.11.0
+ version: 6.19.0(eslint@8.56.0)(typescript@5.3.3)
+ autoprefixer:
+ specifier: ^10.4.14
+ version: 10.4.17(postcss@8.4.33)
+ eslint:
+ specifier: ^8.54.0
+ version: 8.56.0
+ eslint-config-next:
+ specifier: ^14.0.4
+ version: 14.1.0(eslint@8.56.0)(typescript@5.3.3)
+ postcss:
+ specifier: ^8.4.31
+ version: 8.4.33
+ prettier:
+ specifier: ^3.1.0
+ version: 3.2.4
+ prettier-plugin-tailwindcss:
+ specifier: ^0.5.7
+ version: 0.5.11(prettier@3.2.4)
+ prisma:
+ specifier: ^5.9.1
+ version: 5.9.1
+ tailwindcss:
+ specifier: ^3.3.5
+ version: 3.4.1
+ tsx:
+ specifier: ^4.7.1
+ version: 4.7.1
+ typescript:
+ specifier: ^5.1.6
+ version: 5.3.3
+
+packages:
+
+ /@aashutoshrathi/word-wrap@1.2.6:
+ resolution: {integrity: sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==}
+ engines: {node: '>=0.10.0'}
+ dev: true
+
+ /@alloc/quick-lru@5.2.0:
+ resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
+ engines: {node: '>=10'}
+
+ /@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.22
+
+ /@apideck/better-ajv-errors@0.3.6(ajv@8.12.0):
+ resolution: {integrity: sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ ajv: '>=8'
+ dependencies:
+ ajv: 8.12.0
+ json-schema: 0.4.0
+ jsonpointer: 5.0.1
+ leven: 3.1.0
+
+ /@aws-crypto/crc32@3.0.0:
+ resolution: {integrity: sha512-IzSgsrxUcsrejQbPVilIKy16kAT52EwB6zSaI+M3xxIhKh5+aldEyvI+z6erM7TCLB2BJsFrtHjp6/4/sr+3dA==}
+ dependencies:
+ '@aws-crypto/util': 3.0.0
+ '@aws-sdk/types': 3.515.0
+ tslib: 1.14.1
+ dev: false
+
+ /@aws-crypto/crc32c@3.0.0:
+ resolution: {integrity: sha512-ENNPPManmnVJ4BTXlOjAgD7URidbAznURqD0KvfREyc4o20DPYdEldU1f5cQ7Jbj0CJJSPaMIk/9ZshdB3210w==}
+ dependencies:
+ '@aws-crypto/util': 3.0.0
+ '@aws-sdk/types': 3.515.0
+ tslib: 1.14.1
+ dev: false
+
+ /@aws-crypto/ie11-detection@3.0.0:
+ resolution: {integrity: sha512-341lBBkiY1DfDNKai/wXM3aujNBkXR7tq1URPQDL9wi3AUbI80NR74uF1TXHMm7po1AcnFk8iu2S2IeU/+/A+Q==}
+ dependencies:
+ tslib: 1.14.1
+ dev: false
+
+ /@aws-crypto/sha1-browser@3.0.0:
+ resolution: {integrity: sha512-NJth5c997GLHs6nOYTzFKTbYdMNA6/1XlKVgnZoaZcQ7z7UJlOgj2JdbHE8tiYLS3fzXNCguct77SPGat2raSw==}
+ dependencies:
+ '@aws-crypto/ie11-detection': 3.0.0
+ '@aws-crypto/supports-web-crypto': 3.0.0
+ '@aws-crypto/util': 3.0.0
+ '@aws-sdk/types': 3.515.0
+ '@aws-sdk/util-locate-window': 3.495.0
+ '@aws-sdk/util-utf8-browser': 3.259.0
+ tslib: 1.14.1
+ dev: false
+
+ /@aws-crypto/sha256-browser@3.0.0:
+ resolution: {integrity: sha512-8VLmW2B+gjFbU5uMeqtQM6Nj0/F1bro80xQXCW6CQBWgosFWXTx77aeOF5CAIAmbOK64SdMBJdNr6J41yP5mvQ==}
+ dependencies:
+ '@aws-crypto/ie11-detection': 3.0.0
+ '@aws-crypto/sha256-js': 3.0.0
+ '@aws-crypto/supports-web-crypto': 3.0.0
+ '@aws-crypto/util': 3.0.0
+ '@aws-sdk/types': 3.515.0
+ '@aws-sdk/util-locate-window': 3.495.0
+ '@aws-sdk/util-utf8-browser': 3.259.0
+ tslib: 1.14.1
+ dev: false
+
+ /@aws-crypto/sha256-js@3.0.0:
+ resolution: {integrity: sha512-PnNN7os0+yd1XvXAy23CFOmTbMaDxgxXtTKHybrJ39Y8kGzBATgBFibWJKH6BhytLI/Zyszs87xCOBNyBig6vQ==}
+ dependencies:
+ '@aws-crypto/util': 3.0.0
+ '@aws-sdk/types': 3.515.0
+ tslib: 1.14.1
+ dev: false
+
+ /@aws-crypto/supports-web-crypto@3.0.0:
+ resolution: {integrity: sha512-06hBdMwUAb2WFTuGG73LSC0wfPu93xWwo5vL2et9eymgmu3Id5vFAHBbajVWiGhPO37qcsdCap/FqXvJGJWPIg==}
+ dependencies:
+ tslib: 1.14.1
+ dev: false
+
+ /@aws-crypto/util@3.0.0:
+ resolution: {integrity: sha512-2OJlpeJpCR48CC8r+uKVChzs9Iungj9wkZrl8Z041DWEWvyIHILYKCPNzJghKsivj+S3mLo6BVc7mBNzdxA46w==}
+ dependencies:
+ '@aws-sdk/types': 3.515.0
+ '@aws-sdk/util-utf8-browser': 3.259.0
+ tslib: 1.14.1
+ dev: false
+
+ /@aws-sdk/client-s3@3.515.0:
+ resolution: {integrity: sha512-K527n83hrMUdosxOYTzL63wtlJtmN5SUJZnGY1sUR6UyOrnOr9lS6t3AB6BgHqLFRFZJqSqmhflv2cOD7P1UPg==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@aws-crypto/sha1-browser': 3.0.0
+ '@aws-crypto/sha256-browser': 3.0.0
+ '@aws-crypto/sha256-js': 3.0.0
+ '@aws-sdk/client-sts': 3.515.0(@aws-sdk/credential-provider-node@3.515.0)
+ '@aws-sdk/core': 3.513.0
+ '@aws-sdk/credential-provider-node': 3.515.0
+ '@aws-sdk/middleware-bucket-endpoint': 3.515.0
+ '@aws-sdk/middleware-expect-continue': 3.515.0
+ '@aws-sdk/middleware-flexible-checksums': 3.515.0
+ '@aws-sdk/middleware-host-header': 3.515.0
+ '@aws-sdk/middleware-location-constraint': 3.515.0
+ '@aws-sdk/middleware-logger': 3.515.0
+ '@aws-sdk/middleware-recursion-detection': 3.515.0
+ '@aws-sdk/middleware-sdk-s3': 3.515.0
+ '@aws-sdk/middleware-signing': 3.515.0
+ '@aws-sdk/middleware-ssec': 3.515.0
+ '@aws-sdk/middleware-user-agent': 3.515.0
+ '@aws-sdk/region-config-resolver': 3.515.0
+ '@aws-sdk/signature-v4-multi-region': 3.515.0
+ '@aws-sdk/types': 3.515.0
+ '@aws-sdk/util-endpoints': 3.515.0
+ '@aws-sdk/util-user-agent-browser': 3.515.0
+ '@aws-sdk/util-user-agent-node': 3.515.0
+ '@aws-sdk/xml-builder': 3.496.0
+ '@smithy/config-resolver': 2.1.1
+ '@smithy/core': 1.3.2
+ '@smithy/eventstream-serde-browser': 2.1.1
+ '@smithy/eventstream-serde-config-resolver': 2.1.1
+ '@smithy/eventstream-serde-node': 2.1.1
+ '@smithy/fetch-http-handler': 2.4.1
+ '@smithy/hash-blob-browser': 2.1.1
+ '@smithy/hash-node': 2.1.1
+ '@smithy/hash-stream-node': 2.1.1
+ '@smithy/invalid-dependency': 2.1.1
+ '@smithy/md5-js': 2.1.1
+ '@smithy/middleware-content-length': 2.1.1
+ '@smithy/middleware-endpoint': 2.4.1
+ '@smithy/middleware-retry': 2.1.1
+ '@smithy/middleware-serde': 2.1.1
+ '@smithy/middleware-stack': 2.1.1
+ '@smithy/node-config-provider': 2.2.1
+ '@smithy/node-http-handler': 2.3.1
+ '@smithy/protocol-http': 3.1.1
+ '@smithy/smithy-client': 2.3.1
+ '@smithy/types': 2.9.1
+ '@smithy/url-parser': 2.1.1
+ '@smithy/util-base64': 2.1.1
+ '@smithy/util-body-length-browser': 2.1.1
+ '@smithy/util-body-length-node': 2.2.1
+ '@smithy/util-defaults-mode-browser': 2.1.1
+ '@smithy/util-defaults-mode-node': 2.2.0
+ '@smithy/util-endpoints': 1.1.1
+ '@smithy/util-retry': 2.1.1
+ '@smithy/util-stream': 2.1.1
+ '@smithy/util-utf8': 2.1.1
+ '@smithy/util-waiter': 2.1.1
+ fast-xml-parser: 4.2.5
+ tslib: 2.6.2
+ transitivePeerDependencies:
+ - aws-crt
+ dev: false
+
+ /@aws-sdk/client-sso-oidc@3.515.0(@aws-sdk/credential-provider-node@3.515.0):
+ resolution: {integrity: sha512-zACa8LNlPUdlNUBqQRf5a3MfouLNtcBfm84v2c8M976DwJrMGONPe1QjyLLsD38uESQiXiVQRruj/b000iMXNw==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ '@aws-sdk/credential-provider-node': ^3.515.0
+ dependencies:
+ '@aws-crypto/sha256-browser': 3.0.0
+ '@aws-crypto/sha256-js': 3.0.0
+ '@aws-sdk/client-sts': 3.515.0(@aws-sdk/credential-provider-node@3.515.0)
+ '@aws-sdk/core': 3.513.0
+ '@aws-sdk/credential-provider-node': 3.515.0
+ '@aws-sdk/middleware-host-header': 3.515.0
+ '@aws-sdk/middleware-logger': 3.515.0
+ '@aws-sdk/middleware-recursion-detection': 3.515.0
+ '@aws-sdk/middleware-user-agent': 3.515.0
+ '@aws-sdk/region-config-resolver': 3.515.0
+ '@aws-sdk/types': 3.515.0
+ '@aws-sdk/util-endpoints': 3.515.0
+ '@aws-sdk/util-user-agent-browser': 3.515.0
+ '@aws-sdk/util-user-agent-node': 3.515.0
+ '@smithy/config-resolver': 2.1.1
+ '@smithy/core': 1.3.2
+ '@smithy/fetch-http-handler': 2.4.1
+ '@smithy/hash-node': 2.1.1
+ '@smithy/invalid-dependency': 2.1.1
+ '@smithy/middleware-content-length': 2.1.1
+ '@smithy/middleware-endpoint': 2.4.1
+ '@smithy/middleware-retry': 2.1.1
+ '@smithy/middleware-serde': 2.1.1
+ '@smithy/middleware-stack': 2.1.1
+ '@smithy/node-config-provider': 2.2.1
+ '@smithy/node-http-handler': 2.3.1
+ '@smithy/protocol-http': 3.1.1
+ '@smithy/smithy-client': 2.3.1
+ '@smithy/types': 2.9.1
+ '@smithy/url-parser': 2.1.1
+ '@smithy/util-base64': 2.1.1
+ '@smithy/util-body-length-browser': 2.1.1
+ '@smithy/util-body-length-node': 2.2.1
+ '@smithy/util-defaults-mode-browser': 2.1.1
+ '@smithy/util-defaults-mode-node': 2.2.0
+ '@smithy/util-endpoints': 1.1.1
+ '@smithy/util-middleware': 2.1.1
+ '@smithy/util-retry': 2.1.1
+ '@smithy/util-utf8': 2.1.1
+ tslib: 2.6.2
+ transitivePeerDependencies:
+ - aws-crt
+ dev: false
+
+ /@aws-sdk/client-sso@3.515.0:
+ resolution: {integrity: sha512-4oGBLW476zmkdN98lAns3bObRNO+DLOfg4MDUSR6l6GYBV/zGAtoy2O/FhwYKgA2L5h2ZtElGopLlk/1Q0ePLw==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@aws-crypto/sha256-browser': 3.0.0
+ '@aws-crypto/sha256-js': 3.0.0
+ '@aws-sdk/core': 3.513.0
+ '@aws-sdk/middleware-host-header': 3.515.0
+ '@aws-sdk/middleware-logger': 3.515.0
+ '@aws-sdk/middleware-recursion-detection': 3.515.0
+ '@aws-sdk/middleware-user-agent': 3.515.0
+ '@aws-sdk/region-config-resolver': 3.515.0
+ '@aws-sdk/types': 3.515.0
+ '@aws-sdk/util-endpoints': 3.515.0
+ '@aws-sdk/util-user-agent-browser': 3.515.0
+ '@aws-sdk/util-user-agent-node': 3.515.0
+ '@smithy/config-resolver': 2.1.1
+ '@smithy/core': 1.3.2
+ '@smithy/fetch-http-handler': 2.4.1
+ '@smithy/hash-node': 2.1.1
+ '@smithy/invalid-dependency': 2.1.1
+ '@smithy/middleware-content-length': 2.1.1
+ '@smithy/middleware-endpoint': 2.4.1
+ '@smithy/middleware-retry': 2.1.1
+ '@smithy/middleware-serde': 2.1.1
+ '@smithy/middleware-stack': 2.1.1
+ '@smithy/node-config-provider': 2.2.1
+ '@smithy/node-http-handler': 2.3.1
+ '@smithy/protocol-http': 3.1.1
+ '@smithy/smithy-client': 2.3.1
+ '@smithy/types': 2.9.1
+ '@smithy/url-parser': 2.1.1
+ '@smithy/util-base64': 2.1.1
+ '@smithy/util-body-length-browser': 2.1.1
+ '@smithy/util-body-length-node': 2.2.1
+ '@smithy/util-defaults-mode-browser': 2.1.1
+ '@smithy/util-defaults-mode-node': 2.2.0
+ '@smithy/util-endpoints': 1.1.1
+ '@smithy/util-middleware': 2.1.1
+ '@smithy/util-retry': 2.1.1
+ '@smithy/util-utf8': 2.1.1
+ tslib: 2.6.2
+ transitivePeerDependencies:
+ - aws-crt
+ dev: false
+
+ /@aws-sdk/client-sts@3.515.0(@aws-sdk/credential-provider-node@3.515.0):
+ resolution: {integrity: sha512-ScYuvaIDgip3atOJIA1FU2n0gJkEdveu1KrrCPathoUCV5zpK8qQmO/n+Fj/7hKFxeKdFbB+4W4CsJWYH94nlg==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ '@aws-sdk/credential-provider-node': ^3.515.0
+ dependencies:
+ '@aws-crypto/sha256-browser': 3.0.0
+ '@aws-crypto/sha256-js': 3.0.0
+ '@aws-sdk/core': 3.513.0
+ '@aws-sdk/credential-provider-node': 3.515.0
+ '@aws-sdk/middleware-host-header': 3.515.0
+ '@aws-sdk/middleware-logger': 3.515.0
+ '@aws-sdk/middleware-recursion-detection': 3.515.0
+ '@aws-sdk/middleware-user-agent': 3.515.0
+ '@aws-sdk/region-config-resolver': 3.515.0
+ '@aws-sdk/types': 3.515.0
+ '@aws-sdk/util-endpoints': 3.515.0
+ '@aws-sdk/util-user-agent-browser': 3.515.0
+ '@aws-sdk/util-user-agent-node': 3.515.0
+ '@smithy/config-resolver': 2.1.1
+ '@smithy/core': 1.3.2
+ '@smithy/fetch-http-handler': 2.4.1
+ '@smithy/hash-node': 2.1.1
+ '@smithy/invalid-dependency': 2.1.1
+ '@smithy/middleware-content-length': 2.1.1
+ '@smithy/middleware-endpoint': 2.4.1
+ '@smithy/middleware-retry': 2.1.1
+ '@smithy/middleware-serde': 2.1.1
+ '@smithy/middleware-stack': 2.1.1
+ '@smithy/node-config-provider': 2.2.1
+ '@smithy/node-http-handler': 2.3.1
+ '@smithy/protocol-http': 3.1.1
+ '@smithy/smithy-client': 2.3.1
+ '@smithy/types': 2.9.1
+ '@smithy/url-parser': 2.1.1
+ '@smithy/util-base64': 2.1.1
+ '@smithy/util-body-length-browser': 2.1.1
+ '@smithy/util-body-length-node': 2.2.1
+ '@smithy/util-defaults-mode-browser': 2.1.1
+ '@smithy/util-defaults-mode-node': 2.2.0
+ '@smithy/util-endpoints': 1.1.1
+ '@smithy/util-middleware': 2.1.1
+ '@smithy/util-retry': 2.1.1
+ '@smithy/util-utf8': 2.1.1
+ fast-xml-parser: 4.2.5
+ tslib: 2.6.2
+ transitivePeerDependencies:
+ - aws-crt
+ dev: false
+
+ /@aws-sdk/core@3.513.0:
+ resolution: {integrity: sha512-L+9DL4apWuqNKVOMJ8siAuWoRM9rZf9w1iPv8S2o83WO2jVK7E/m+rNW1dFo9HsA5V1ccDl2H2qLXx24HiHmOw==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/core': 1.3.2
+ '@smithy/protocol-http': 3.1.1
+ '@smithy/signature-v4': 2.1.1
+ '@smithy/smithy-client': 2.3.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@aws-sdk/credential-provider-env@3.515.0:
+ resolution: {integrity: sha512-45vxdyqhTAaUMERYVWOziG3K8L2TV9G4ryQS/KZ84o7NAybE9GMdoZRVmGHAO7mJJ1wQiYCM/E+i5b3NW9JfNA==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@aws-sdk/types': 3.515.0
+ '@smithy/property-provider': 2.1.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@aws-sdk/credential-provider-http@3.515.0:
+ resolution: {integrity: sha512-Ba6FXK77vU4WyheiamNjEuTFmir0eAXuJGPO27lBaA8g+V/seXGHScsbOG14aQGDOr2P02OPwKGZrWWA7BFpfQ==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@aws-sdk/types': 3.515.0
+ '@smithy/fetch-http-handler': 2.4.1
+ '@smithy/node-http-handler': 2.3.1
+ '@smithy/property-provider': 2.1.1
+ '@smithy/protocol-http': 3.1.1
+ '@smithy/smithy-client': 2.3.1
+ '@smithy/types': 2.9.1
+ '@smithy/util-stream': 2.1.1
+ tslib: 2.6.2
+ dev: false
+
+ /@aws-sdk/credential-provider-ini@3.515.0(@aws-sdk/credential-provider-node@3.515.0):
+ resolution: {integrity: sha512-ouDlNZdv2TKeVEA/YZk2+XklTXyAAGdbWnl4IgN9ItaodWI+lZjdIoNC8BAooVH+atIV/cZgoGTGQL7j2TxJ9A==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@aws-sdk/client-sts': 3.515.0(@aws-sdk/credential-provider-node@3.515.0)
+ '@aws-sdk/credential-provider-env': 3.515.0
+ '@aws-sdk/credential-provider-process': 3.515.0
+ '@aws-sdk/credential-provider-sso': 3.515.0(@aws-sdk/credential-provider-node@3.515.0)
+ '@aws-sdk/credential-provider-web-identity': 3.515.0(@aws-sdk/credential-provider-node@3.515.0)
+ '@aws-sdk/types': 3.515.0
+ '@smithy/credential-provider-imds': 2.2.1
+ '@smithy/property-provider': 2.1.1
+ '@smithy/shared-ini-file-loader': 2.3.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ transitivePeerDependencies:
+ - '@aws-sdk/credential-provider-node'
+ - aws-crt
+ dev: false
+
+ /@aws-sdk/credential-provider-node@3.515.0:
+ resolution: {integrity: sha512-Y4kHSpbxksiCZZNcvsiKUd8Fb2XlyUuONEwqWFNL82ZH6TCCjBGS31wJQCSxBHqYcOL3tiORUEJkoO7uS30uQA==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@aws-sdk/credential-provider-env': 3.515.0
+ '@aws-sdk/credential-provider-http': 3.515.0
+ '@aws-sdk/credential-provider-ini': 3.515.0(@aws-sdk/credential-provider-node@3.515.0)
+ '@aws-sdk/credential-provider-process': 3.515.0
+ '@aws-sdk/credential-provider-sso': 3.515.0(@aws-sdk/credential-provider-node@3.515.0)
+ '@aws-sdk/credential-provider-web-identity': 3.515.0(@aws-sdk/credential-provider-node@3.515.0)
+ '@aws-sdk/types': 3.515.0
+ '@smithy/credential-provider-imds': 2.2.1
+ '@smithy/property-provider': 2.1.1
+ '@smithy/shared-ini-file-loader': 2.3.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ transitivePeerDependencies:
+ - aws-crt
+ dev: false
+
+ /@aws-sdk/credential-provider-process@3.515.0:
+ resolution: {integrity: sha512-pSjiOA2FM63LHRKNDvEpBRp80FVGT0Mw/gzgbqFXP+sewk0WVonYbEcMDTJptH3VsLPGzqH/DQ1YL/aEIBuXFQ==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@aws-sdk/types': 3.515.0
+ '@smithy/property-provider': 2.1.1
+ '@smithy/shared-ini-file-loader': 2.3.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@aws-sdk/credential-provider-sso@3.515.0(@aws-sdk/credential-provider-node@3.515.0):
+ resolution: {integrity: sha512-j7vUkiSmuhpBvZYoPTRTI4ePnQbiZMFl6TNhg9b9DprC1zHkucsZnhRhqjOVlrw/H6J4jmcPGcHHTZ5WQNI5xQ==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@aws-sdk/client-sso': 3.515.0
+ '@aws-sdk/token-providers': 3.515.0(@aws-sdk/credential-provider-node@3.515.0)
+ '@aws-sdk/types': 3.515.0
+ '@smithy/property-provider': 2.1.1
+ '@smithy/shared-ini-file-loader': 2.3.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ transitivePeerDependencies:
+ - '@aws-sdk/credential-provider-node'
+ - aws-crt
+ dev: false
+
+ /@aws-sdk/credential-provider-web-identity@3.515.0(@aws-sdk/credential-provider-node@3.515.0):
+ resolution: {integrity: sha512-66+2g4z3fWwdoGReY8aUHvm6JrKZMTRxjuizljVmMyOBttKPeBYXvUTop/g3ZGUx1f8j+C5qsGK52viYBvtjuQ==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@aws-sdk/client-sts': 3.515.0(@aws-sdk/credential-provider-node@3.515.0)
+ '@aws-sdk/types': 3.515.0
+ '@smithy/property-provider': 2.1.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ transitivePeerDependencies:
+ - '@aws-sdk/credential-provider-node'
+ - aws-crt
+ dev: false
+
+ /@aws-sdk/middleware-bucket-endpoint@3.515.0:
+ resolution: {integrity: sha512-Vm423j3udFrhKPaKiXtie+6aF05efjX8lhAu5VOruIvbam7olvdWNdkH7sGWlz1ko3CVa7PwOYjGHiOOhxpEOA==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@aws-sdk/types': 3.515.0
+ '@aws-sdk/util-arn-parser': 3.495.0
+ '@smithy/node-config-provider': 2.2.1
+ '@smithy/protocol-http': 3.1.1
+ '@smithy/types': 2.9.1
+ '@smithy/util-config-provider': 2.2.1
+ tslib: 2.6.2
+ dev: false
+
+ /@aws-sdk/middleware-expect-continue@3.515.0:
+ resolution: {integrity: sha512-TWCXulivab4reOMx/vxa/IwnPX78fLwI9NUoAxjsqB6W9qjmSnPD43BSVeGvbbl/YNmgk7XfMbZb6IgxW7RyzA==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@aws-sdk/types': 3.515.0
+ '@smithy/protocol-http': 3.1.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@aws-sdk/middleware-flexible-checksums@3.515.0:
+ resolution: {integrity: sha512-ydGjnqNeYlJaAkmQeQnS4pZRAAvzefdm8c234Qh0Fg55xRwHTNLp7uYsdfkTjrdAlj6YIO3Zr6vK6VJ6MGCwug==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@aws-crypto/crc32': 3.0.0
+ '@aws-crypto/crc32c': 3.0.0
+ '@aws-sdk/types': 3.515.0
+ '@smithy/is-array-buffer': 2.1.1
+ '@smithy/protocol-http': 3.1.1
+ '@smithy/types': 2.9.1
+ '@smithy/util-utf8': 2.1.1
+ tslib: 2.6.2
+ dev: false
+
+ /@aws-sdk/middleware-host-header@3.515.0:
+ resolution: {integrity: sha512-I1MwWPzdRKM1luvdDdjdGsDjNVPhj9zaIytEchjTY40NcKOg+p2evLD2y69ozzg8pyXK63r8DdvDGOo9QPuh0A==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@aws-sdk/types': 3.515.0
+ '@smithy/protocol-http': 3.1.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@aws-sdk/middleware-location-constraint@3.515.0:
+ resolution: {integrity: sha512-ORFC5oijjTJsHhUXy9o52/vl5Irf6e83bE/8tBp+sVVx81+E8zTTWZbysoa41c0B5Ycd0H3wCWutvjdXT16ydQ==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@aws-sdk/types': 3.515.0
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@aws-sdk/middleware-logger@3.515.0:
+ resolution: {integrity: sha512-qXomJzg2m/5seQOxHi/yOXOKfSjwrrJSmEmfwJKJyQgdMbBcjz3Cz0H/1LyC6c5hHm6a/SZgSTzDAbAoUmyL+Q==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@aws-sdk/types': 3.515.0
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@aws-sdk/middleware-recursion-detection@3.515.0:
+ resolution: {integrity: sha512-dokHLbTV3IHRIBrw9mGoxcNTnQsjlm7TpkJhPdGT9T4Mq399EyQo51u6IsVMm07RXLl2Zw7u+u9p+qWBFzmFRA==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@aws-sdk/types': 3.515.0
+ '@smithy/protocol-http': 3.1.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@aws-sdk/middleware-sdk-s3@3.515.0:
+ resolution: {integrity: sha512-vB8JwiTEAqm1UT9xfugnCgl0H0dtBLUQQK99JwQEWjHPZmQ3HQuVkykmJRY3X0hzKMEgqXodz0hZOvf3Hq1mvQ==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@aws-sdk/types': 3.515.0
+ '@aws-sdk/util-arn-parser': 3.495.0
+ '@smithy/node-config-provider': 2.2.1
+ '@smithy/protocol-http': 3.1.1
+ '@smithy/signature-v4': 2.1.1
+ '@smithy/smithy-client': 2.3.1
+ '@smithy/types': 2.9.1
+ '@smithy/util-config-provider': 2.2.1
+ tslib: 2.6.2
+ dev: false
+
+ /@aws-sdk/middleware-signing@3.515.0:
+ resolution: {integrity: sha512-SdjCyQCL702I07KhCiBFcoh6+NYtnruHJQIzWwMpBteuYHnCHW1k9uZ6pqacsS+Y6qpAKfTVNpQx2zP2s6QoHA==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@aws-sdk/types': 3.515.0
+ '@smithy/property-provider': 2.1.1
+ '@smithy/protocol-http': 3.1.1
+ '@smithy/signature-v4': 2.1.1
+ '@smithy/types': 2.9.1
+ '@smithy/util-middleware': 2.1.1
+ tslib: 2.6.2
+ dev: false
+
+ /@aws-sdk/middleware-ssec@3.515.0:
+ resolution: {integrity: sha512-0qLjKiorosVBzzaV/o7MEyS9xqLLu02qGbP564Z/FZY74JUQEpBNedgveMUbb6lqr85RnOuwZ0GZ0cBRfH2brQ==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@aws-sdk/types': 3.515.0
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@aws-sdk/middleware-user-agent@3.515.0:
+ resolution: {integrity: sha512-nOqZjGA/GkjuJ5fUshec9Fv6HFd7ovOTxMJbw3MfAhqXuVZ6dKF41lpVJ4imNsgyFt3shUg9WDY8zGFjlYMB3g==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@aws-sdk/types': 3.515.0
+ '@aws-sdk/util-endpoints': 3.515.0
+ '@smithy/protocol-http': 3.1.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@aws-sdk/region-config-resolver@3.515.0:
+ resolution: {integrity: sha512-RIRx9loxMgEAc/r1wPfnfShOuzn4RBi8pPPv6/jhhITEeMnJe6enAh2k5y9DdiVDDgCWZgVFSv0YkAIfzAFsnQ==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@aws-sdk/types': 3.515.0
+ '@smithy/node-config-provider': 2.2.1
+ '@smithy/types': 2.9.1
+ '@smithy/util-config-provider': 2.2.1
+ '@smithy/util-middleware': 2.1.1
+ tslib: 2.6.2
+ dev: false
+
+ /@aws-sdk/s3-request-presigner@3.515.0:
+ resolution: {integrity: sha512-B6RcXWJTOHSqZDII/sYeM89MWc//AwA7iIcZk+oXyUSdVTl03z6raJMxWqY0dPx7KuBjLTnZPqUXKCCoQvnp/g==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@aws-sdk/signature-v4-multi-region': 3.515.0
+ '@aws-sdk/types': 3.515.0
+ '@aws-sdk/util-format-url': 3.515.0
+ '@smithy/middleware-endpoint': 2.4.1
+ '@smithy/protocol-http': 3.1.1
+ '@smithy/smithy-client': 2.3.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@aws-sdk/signature-v4-multi-region@3.515.0:
+ resolution: {integrity: sha512-5lrCn4DSE0zL41k0L6moqcdExZhWdAnV0/oMEagrISzQYoia+aNTEeyVD3xqJhRbEW4gCj3Uoyis6c8muf7b9g==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@aws-sdk/middleware-sdk-s3': 3.515.0
+ '@aws-sdk/types': 3.515.0
+ '@smithy/protocol-http': 3.1.1
+ '@smithy/signature-v4': 2.1.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@aws-sdk/token-providers@3.515.0(@aws-sdk/credential-provider-node@3.515.0):
+ resolution: {integrity: sha512-MQuf04rIcTXqwDzmyHSpFPF1fKEzRl64oXtCRUF3ddxTdK6wxXkePfK6wNCuL+GEbEcJAoCtIGIRpzGPJvQjHA==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@aws-sdk/client-sso-oidc': 3.515.0(@aws-sdk/credential-provider-node@3.515.0)
+ '@aws-sdk/types': 3.515.0
+ '@smithy/property-provider': 2.1.1
+ '@smithy/shared-ini-file-loader': 2.3.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ transitivePeerDependencies:
+ - '@aws-sdk/credential-provider-node'
+ - aws-crt
+ dev: false
+
+ /@aws-sdk/types@3.515.0:
+ resolution: {integrity: sha512-B3gUpiMlpT6ERaLvZZ61D0RyrQPsFYDkCncLPVkZOKkCOoFU46zi1o6T5JcYiz8vkx1q9RGloQ5exh79s5pU/w==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@aws-sdk/util-arn-parser@3.495.0:
+ resolution: {integrity: sha512-hwdA3XAippSEUxs7jpznwD63YYFR+LtQvlEcebPTgWR9oQgG9TfS+39PUfbnEeje1ICuOrN3lrFqFbmP9uzbMg==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ tslib: 2.6.2
+ dev: false
+
+ /@aws-sdk/util-endpoints@3.515.0:
+ resolution: {integrity: sha512-UJi+jdwcGFV/F7d3+e2aQn5yZOVpDiAgfgNhPnEtgV0WozJ5/ZUeZBgWvSc/K415N4A4D/9cbBc7+I+35qzcDQ==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@aws-sdk/types': 3.515.0
+ '@smithy/types': 2.9.1
+ '@smithy/util-endpoints': 1.1.1
+ tslib: 2.6.2
+ dev: false
+
+ /@aws-sdk/util-format-url@3.515.0:
+ resolution: {integrity: sha512-7BgmUldmECebZU2qUAxOoEkHnji5NZX/j6TcgY4xgl1tUycw72BeKdcQYLUt4YoXQmIGZHiBL8L/TfO48W+FpA==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@aws-sdk/types': 3.515.0
+ '@smithy/querystring-builder': 2.1.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@aws-sdk/util-locate-window@3.495.0:
+ resolution: {integrity: sha512-MfaPXT0kLX2tQaR90saBT9fWQq2DHqSSJRzW+MZWsmF+y5LGCOhO22ac/2o6TKSQm7h0HRc2GaADqYYYor62yg==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ tslib: 2.6.2
+ dev: false
+
+ /@aws-sdk/util-user-agent-browser@3.515.0:
+ resolution: {integrity: sha512-pTWQb0JCafTmLHLDv3Qqs/nAAJghcPdGQIBpsCStb0YEzg3At/dOi2AIQ683yYnXmeOxLXJDzmlsovfVObJScw==}
+ dependencies:
+ '@aws-sdk/types': 3.515.0
+ '@smithy/types': 2.9.1
+ bowser: 2.11.0
+ tslib: 2.6.2
+ dev: false
+
+ /@aws-sdk/util-user-agent-node@3.515.0:
+ resolution: {integrity: sha512-A/KJ+/HTohHyVXLH+t/bO0Z2mPrQgELbQO8tX+B2nElo8uklj70r5cT7F8ETsI9oOy+HDVpiL5/v45ZgpUOiPg==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ aws-crt: '>=1.0.0'
+ peerDependenciesMeta:
+ aws-crt:
+ optional: true
+ dependencies:
+ '@aws-sdk/types': 3.515.0
+ '@smithy/node-config-provider': 2.2.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@aws-sdk/util-utf8-browser@3.259.0:
+ resolution: {integrity: sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==}
+ dependencies:
+ tslib: 2.6.2
+ dev: false
+
+ /@aws-sdk/xml-builder@3.496.0:
+ resolution: {integrity: sha512-GvEjh537IIeOw1ZkZuB37sV12u+ipS5Z1dwjEC/HAvhl5ac23ULtTr1/n+U1gLNN+BAKSWjKiQ2ksj8DiUzeyw==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@babel/code-frame@7.23.5:
+ resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/highlight': 7.23.4
+ chalk: 2.4.2
+
+ /@babel/compat-data@7.23.5:
+ resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==}
+ engines: {node: '>=6.9.0'}
+
+ /@babel/core@7.23.7:
+ resolution: {integrity: sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@ampproject/remapping': 2.2.1
+ '@babel/code-frame': 7.23.5
+ '@babel/generator': 7.23.6
+ '@babel/helper-compilation-targets': 7.23.6
+ '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.7)
+ '@babel/helpers': 7.23.8
+ '@babel/parser': 7.23.6
+ '@babel/template': 7.22.15
+ '@babel/traverse': 7.23.7
+ '@babel/types': 7.23.6
+ convert-source-map: 2.0.0
+ debug: 4.3.4
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ /@babel/core@7.23.9:
+ resolution: {integrity: sha512-5q0175NOjddqpvvzU+kDiSOAk4PfdO6FvwCWoQ6RO7rTzEe8vlo+4HVfcnAREhD4npMs0e9uZypjTwzZPCf/cw==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@ampproject/remapping': 2.2.1
+ '@babel/code-frame': 7.23.5
+ '@babel/generator': 7.23.6
+ '@babel/helper-compilation-targets': 7.23.6
+ '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.9)
+ '@babel/helpers': 7.23.9
+ '@babel/parser': 7.23.9
+ '@babel/template': 7.23.9
+ '@babel/traverse': 7.23.9
+ '@babel/types': 7.23.9
+ 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/generator@7.23.6:
+ resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/types': 7.23.6
+ '@jridgewell/gen-mapping': 0.3.3
+ '@jridgewell/trace-mapping': 0.3.22
+ jsesc: 2.5.2
+
+ /@babel/helper-annotate-as-pure@7.22.5:
+ resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/types': 7.23.6
+
+ /@babel/helper-builder-binary-assignment-operator-visitor@7.22.15:
+ resolution: {integrity: sha512-QkBXwGgaoC2GtGZRoma6kv7Szfv06khvhFav67ZExau2RaXzy8MpHSMO2PNoP2XtmQphJQRHFfg77Bq731Yizw==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/types': 7.23.6
+
+ /@babel/helper-compilation-targets@7.23.6:
+ resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/compat-data': 7.23.5
+ '@babel/helper-validator-option': 7.23.5
+ browserslist: 4.22.2
+ lru-cache: 5.1.1
+ semver: 6.3.1
+
+ /@babel/helper-create-class-features-plugin@7.23.7(@babel/core@7.23.7):
+ resolution: {integrity: sha512-xCoqR/8+BoNnXOY7RVSgv6X+o7pmT5q1d+gGcRlXYkI+9B31glE4jeejhKVpA04O1AtzOt7OSQ6VYKP5FcRl9g==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+ dependencies:
+ '@babel/core': 7.23.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.23.0
+ '@babel/helper-optimise-call-expression': 7.22.5
+ '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.7)
+ '@babel/helper-skip-transparent-expression-wrappers': 7.22.5
+ '@babel/helper-split-export-declaration': 7.22.6
+ semver: 6.3.1
+
+ /@babel/helper-create-regexp-features-plugin@7.22.15(@babel/core@7.23.7):
+ resolution: {integrity: sha512-29FkPLFjn4TPEa3RE7GpW+qbE8tlsu3jntNYNfcGsc49LphF1PQIiD+vMZ1z1xVOKt+93khA9tc2JBs3kBjA7w==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-annotate-as-pure': 7.22.5
+ regexpu-core: 5.3.2
+ semver: 6.3.1
+
+ /@babel/helper-define-polyfill-provider@0.4.4(@babel/core@7.23.7):
+ resolution: {integrity: sha512-QcJMILQCu2jm5TFPGA3lCpJJTeEP+mqeXooG/NZbg/h5FTFi6V0+99ahlRsW8/kRLyb24LZVCCiclDedhLKcBA==}
+ peerDependencies:
+ '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-compilation-targets': 7.23.6
+ '@babel/helper-plugin-utils': 7.22.5
+ debug: 4.3.4
+ lodash.debounce: 4.0.8
+ resolve: 1.22.8
+ transitivePeerDependencies:
+ - supports-color
+
+ /@babel/helper-define-polyfill-provider@0.5.0(@babel/core@7.23.7):
+ resolution: {integrity: sha512-NovQquuQLAQ5HuyjCz7WQP9MjRj7dx++yspwiyUiGl9ZyadHRSql1HZh5ogRd8W8w6YM6EQ/NTB8rgjLt5W65Q==}
+ peerDependencies:
+ '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-compilation-targets': 7.23.6
+ '@babel/helper-plugin-utils': 7.22.5
+ debug: 4.3.4
+ lodash.debounce: 4.0.8
+ resolve: 1.22.8
+ transitivePeerDependencies:
+ - supports-color
+
+ /@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.6
+
+ /@babel/helper-hoist-variables@7.22.5:
+ resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/types': 7.23.6
+
+ /@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.6
+
+ /@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.6
+
+ /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@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-module-transforms@7.23.3(@babel/core@7.23.9):
+ resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+ dependencies:
+ '@babel/core': 7.23.9
+ '@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.6
+
+ /@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.7):
+ resolution: {integrity: sha512-pBGyV4uBqOns+0UvhsTO8qgl8hO89PmiDYv+/COyp1aeMcmfrfruz+/nCMFiYyFF/Knn0yfrC85ZzNFjembFTw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-annotate-as-pure': 7.22.5
+ '@babel/helper-environment-visitor': 7.22.20
+ '@babel/helper-wrap-function': 7.22.20
+
+ /@babel/helper-replace-supers@7.22.20(@babel/core@7.23.7):
+ resolution: {integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-environment-visitor': 7.22.20
+ '@babel/helper-member-expression-to-functions': 7.23.0
+ '@babel/helper-optimise-call-expression': 7.22.5
+
+ /@babel/helper-simple-access@7.22.5:
+ resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/types': 7.23.6
+
+ /@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.6
+
+ /@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.6
+
+ /@babel/helper-string-parser@7.23.4:
+ resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==}
+ 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.23.5:
+ resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==}
+ 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.6
+
+ /@babel/helpers@7.23.8:
+ resolution: {integrity: sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/template': 7.22.15
+ '@babel/traverse': 7.23.7
+ '@babel/types': 7.23.6
+ transitivePeerDependencies:
+ - supports-color
+
+ /@babel/helpers@7.23.9:
+ resolution: {integrity: sha512-87ICKgU5t5SzOT7sBMfCOZQ2rHjRU+Pcb9BoILMYz600W6DkVRLFBPwQ18gwUVvggqXivaUakpnxWQGbpywbBQ==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/template': 7.23.9
+ '@babel/traverse': 7.23.9
+ '@babel/types': 7.23.9
+ transitivePeerDependencies:
+ - supports-color
+
+ /@babel/highlight@7.23.4:
+ resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/helper-validator-identifier': 7.22.20
+ chalk: 2.4.2
+ js-tokens: 4.0.0
+
+ /@babel/parser@7.23.6:
+ resolution: {integrity: sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+ dependencies:
+ '@babel/types': 7.23.6
+
+ /@babel/parser@7.23.9:
+ resolution: {integrity: sha512-9tcKgqKbs3xGJ+NtKF2ndOBBLVwPjl1SHxPQkd36r3Dlirw3xWUeGaTbqr7uGZcTaxkVNwc+03SVP7aCdWrTlA==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+ dependencies:
+ '@babel/types': 7.23.9
+
+ /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-iRkKcCqb7iGnq9+3G6rZ+Ciz5VywC4XNRHe57lKM+jOeYAoR0lVqdeeDRfh0tQcTfw/+vBhHn926FmQhLtlFLQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-WwlxbfMNdVEpQjZmK5mhm7oSwD3dS6eU+Iwsi4Knl9wAletWem7kaRsGOG+8UEbRyqxY4SS5zvtfXwX+jMxUwQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.13.0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+ '@babel/helper-skip-transparent-expression-wrappers': 7.22.5
+ '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.23.7)
+
+ /@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.23.7(@babel/core@7.23.7):
+ resolution: {integrity: sha512-LlRT7HgaifEpQA1ZgLVOIJZZFVPWN5iReq/7/JixwBtwcoeVGDBD53ZV28rrsLYOZs1Y/EHhA8N/Z6aazHR8cw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-environment-visitor': 7.22.20
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.7):
+ resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+
+ /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.23.7):
+ resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.7):
+ resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.23.7):
+ resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-syntax-import-assertions@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-lPgDSU+SJLK3xmFDTV2ZRQAiM7UuUjGidwBywFavObCiZc1BeAAcMtHJKUya92hPHO+at63JJPLygilZard8jw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-syntax-import-attributes@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-pawnE0P9g10xgoP7yKr6CK63K2FMsTE+FZidZO/1PwRdzmAPVs+HS1mAURUsgaoxammTJvULUdIkEK0gOcU2tA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.23.7):
+ resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.23.7):
+ resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.23.7):
+ resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.23.7):
+ resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.23.7):
+ resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.23.7):
+ resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.7)
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-arrow-functions@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-NzQcQrzaQPkaEwoTm4Mhyl8jI1huEL/WWIEvudjTCMJ9aBZNpsJbMASx7EQECtQQPS/DcnFpo0FIh3LvEO9cxQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-async-generator-functions@7.23.7(@babel/core@7.23.7):
+ resolution: {integrity: sha512-PdxEpL71bJp1byMG0va5gwQcXHxuEYC/BgI/e88mGTtohbZN28O5Yit0Plkkm/dBzCF/BxmbNcses1RH1T+urA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@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.7)
+ '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.7)
+
+ /@babel/plugin-transform-async-to-generator@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-A7LFsKi4U4fomjqXJlZg/u0ft/n8/7n7lpffUP/ZULx/DtV9SGlNKZolHH6PE8Xl1ngCc0M11OaeZptXVkfKSw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@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.7)
+
+ /@babel/plugin-transform-block-scoped-functions@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-vI+0sIaPIO6CNuM9Kk5VmXcMVRiOpDh7w2zZt9GXzmE/9KD70CUEVhvPR/etAeNK/FAEkhxQtXOzVF3EuRL41A==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-block-scoping@7.23.4(@babel/core@7.23.7):
+ resolution: {integrity: sha512-0QqbP6B6HOh7/8iNR4CQU2Th/bbRtBp4KS9vcaZd1fZ0wSh5Fyssg0UCIHwxh+ka+pNDREbVLQnHCMHKZfPwfw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-class-properties@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-uM+AN8yCIjDPccsKGlw271xjJtGii+xQIF/uMPS8H15L12jZTsLfF4o5vNO7d/oUguOyfdikHGc/yi9ge4SGIg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-create-class-features-plugin': 7.23.7(@babel/core@7.23.7)
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-class-static-block@7.23.4(@babel/core@7.23.7):
+ resolution: {integrity: sha512-nsWu/1M+ggti1SOALj3hfx5FXzAY06fwPJsUZD4/A5e1bWi46VUIWtD+kOX6/IdhXGsXBWllLFDSnqSCdUNydQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.12.0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-create-class-features-plugin': 7.23.7(@babel/core@7.23.7)
+ '@babel/helper-plugin-utils': 7.22.5
+ '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.7)
+
+ /@babel/plugin-transform-classes@7.23.8(@babel/core@7.23.7):
+ resolution: {integrity: sha512-yAYslGsY1bX6Knmg46RjiCiNSwJKv2IUC8qOdYKqMMr0491SXFhcHqOdRDeCRohOOIzwN/90C6mQ9qAKgrP7dg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-annotate-as-pure': 7.22.5
+ '@babel/helper-compilation-targets': 7.23.6
+ '@babel/helper-environment-visitor': 7.22.20
+ '@babel/helper-function-name': 7.23.0
+ '@babel/helper-plugin-utils': 7.22.5
+ '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.7)
+ '@babel/helper-split-export-declaration': 7.22.6
+ globals: 11.12.0
+
+ /@babel/plugin-transform-computed-properties@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-dTj83UVTLw/+nbiHqQSFdwO9CbTtwq1DsDqm3CUEtDrZNET5rT5E6bIdTlOftDTDLMYxvxHNEYO4B9SLl8SLZw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+ '@babel/template': 7.22.15
+
+ /@babel/plugin-transform-destructuring@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-n225npDqjDIr967cMScVKHXJs7rout1q+tt50inyBCPkyZ8KxeI6d+GIbSBTT/w/9WdlWDOej3V9HE5Lgk57gw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-dotall-regex@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-vgnFYDHAKzFaTVp+mneDsIEbnJ2Np/9ng9iviHw3P/KVcgONxpNULEW/51Z/BaFojG2GI2GwwXck5uV1+1NOYQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.7)
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-duplicate-keys@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-RrqQ+BQmU3Oyav3J+7/myfvRCq7Tbz+kKLLshUmMwNlDHExbGL7ARhajvoBJEvc+fCguPPu887N+3RRXBVKZUA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-dynamic-import@7.23.4(@babel/core@7.23.7):
+ resolution: {integrity: sha512-V6jIbLhdJK86MaLh4Jpghi8ho5fGzt3imHOBu/x0jlBaPYqDoWz4RDXjmMOfnh+JWNaQleEAByZLV0QzBT4YQQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+ '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.7)
+
+ /@babel/plugin-transform-exponentiation-operator@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-5fhCsl1odX96u7ILKHBj4/Y8vipoqwsJMh4csSA8qFfxrZDEA4Ssku2DyNvMJSmZNOEBT750LfFPbtrnTP90BQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-builder-binary-assignment-operator-visitor': 7.22.15
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-export-namespace-from@7.23.4(@babel/core@7.23.7):
+ resolution: {integrity: sha512-GzuSBcKkx62dGzZI1WVgTWvkkz84FZO5TC5T8dl/Tht/rAla6Dg/Mz9Yhypg+ezVACf/rgDuQt3kbWEv7LdUDQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+ '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.7)
+
+ /@babel/plugin-transform-for-of@7.23.6(@babel/core@7.23.7):
+ resolution: {integrity: sha512-aYH4ytZ0qSuBbpfhuofbg/e96oQ7U2w1Aw/UQmKT+1l39uEhUPoFS3fHevDc1G0OvewyDudfMKY1OulczHzWIw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+ '@babel/helper-skip-transparent-expression-wrappers': 7.22.5
+
+ /@babel/plugin-transform-function-name@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-I1QXp1LxIvt8yLaib49dRW5Okt7Q4oaxao6tFVKS/anCdEOMtYwWVKoiOA1p34GOWIZjUK0E+zCp7+l1pfQyiw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-compilation-targets': 7.23.6
+ '@babel/helper-function-name': 7.23.0
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-json-strings@7.23.4(@babel/core@7.23.7):
+ resolution: {integrity: sha512-81nTOqM1dMwZ/aRXQ59zVubN9wHGqk6UtqRK+/q+ciXmRy8fSolhGVvG09HHRGo4l6fr/c4ZhXUQH0uFW7PZbg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+ '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.7)
+
+ /@babel/plugin-transform-literals@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-wZ0PIXRxnwZvl9AYpqNUxpZ5BiTGrYt7kueGQ+N5FiQ7RCOD4cm8iShd6S6ggfVIWaJf2EMk8eRzAh52RfP4rQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-logical-assignment-operators@7.23.4(@babel/core@7.23.7):
+ resolution: {integrity: sha512-Mc/ALf1rmZTP4JKKEhUwiORU+vcfarFVLfcFiolKUo6sewoxSEgl36ak5t+4WamRsNr6nzjZXQjM35WsU+9vbg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+ '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.7)
+
+ /@babel/plugin-transform-member-expression-literals@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-sC3LdDBDi5x96LA+Ytekz2ZPk8i/Ck+DEuDbRAll5rknJ5XRTSaPKEYwomLcs1AA8wg9b3KjIQRsnApj+q51Ag==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-modules-amd@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-vJYQGxeKM4t8hYCKVBlZX/gtIY2I7mRGFNcm85sgXGMTBcoV3QdVtdpbcWEbzbfUIUZKwvgFT82mRvaQIebZzw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.7)
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.7)
+ '@babel/helper-plugin-utils': 7.22.5
+ '@babel/helper-simple-access': 7.22.5
+
+ /@babel/plugin-transform-modules-systemjs@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-ZxyKGTkF9xT9YJuKQRo19ewf3pXpopuYQd8cDXqNzc3mUNbOME0RKMoZxviQk74hwzfQsEe66dE92MaZbdHKNQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-hoist-variables': 7.22.5
+ '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.7)
+ '@babel/helper-plugin-utils': 7.22.5
+ '@babel/helper-validator-identifier': 7.22.20
+
+ /@babel/plugin-transform-modules-umd@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-zHsy9iXX2nIsCBFPud3jKn1IRPWg3Ing1qOZgeKV39m1ZgIdpJqvlWVeiHBZC6ITRG0MfskhYe9cLgntfSFPIg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.7)
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-named-capturing-groups-regex@7.22.5(@babel/core@7.23.7):
+ resolution: {integrity: sha512-YgLLKmS3aUBhHaxp5hi1WJTgOUb/NCuDHzGT9z9WTt3YG+CPRhJs6nprbStx6DnWM4dh6gt7SU3sZodbZ08adQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.7)
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-new-target@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-YJ3xKqtJMAT5/TIZnpAR3I+K+WaDowYbN3xyxI8zxx/Gsypwf9B9h0VB+1Nh6ACAAPRS5NSRje0uVv5i79HYGQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-nullish-coalescing-operator@7.23.4(@babel/core@7.23.7):
+ resolution: {integrity: sha512-jHE9EVVqHKAQx+VePv5LLGHjmHSJR76vawFPTdlxR/LVJPfOEGxREQwQfjuZEOPTwG92X3LINSh3M40Rv4zpVA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+ '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.7)
+
+ /@babel/plugin-transform-numeric-separator@7.23.4(@babel/core@7.23.7):
+ resolution: {integrity: sha512-mps6auzgwjRrwKEZA05cOwuDc9FAzoyFS4ZsG/8F43bTLf/TgkJg7QXOrPO1JO599iA3qgK9MXdMGOEC8O1h6Q==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+ '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.7)
+
+ /@babel/plugin-transform-object-rest-spread@7.23.4(@babel/core@7.23.7):
+ resolution: {integrity: sha512-9x9K1YyeQVw0iOXJlIzwm8ltobIIv7j2iLyP2jIhEbqPRQ7ScNgwQufU2I0Gq11VjyG4gI4yMXt2VFags+1N3g==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/compat-data': 7.23.5
+ '@babel/core': 7.23.7
+ '@babel/helper-compilation-targets': 7.23.6
+ '@babel/helper-plugin-utils': 7.22.5
+ '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.23.7)
+
+ /@babel/plugin-transform-object-super@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-BwQ8q0x2JG+3lxCVFohg+KbQM7plfpBwThdW9A6TMtWwLsbDA01Ek2Zb/AgDN39BiZsExm4qrXxjk+P1/fzGrA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+ '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.7)
+
+ /@babel/plugin-transform-optional-catch-binding@7.23.4(@babel/core@7.23.7):
+ resolution: {integrity: sha512-XIq8t0rJPHf6Wvmbn9nFxU6ao4c7WhghTR5WyV8SrJfUFzyxhCm4nhC+iAp3HFhbAKLfYpgzhJ6t4XCtVwqO5A==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+ '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.7)
+
+ /@babel/plugin-transform-optional-chaining@7.23.4(@babel/core@7.23.7):
+ resolution: {integrity: sha512-ZU8y5zWOfjM5vZ+asjgAPwDaBjJzgufjES89Rs4Lpq63O300R/kOz30WCLo6BxxX6QVEilwSlpClnG5cZaikTA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@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.7)
+
+ /@babel/plugin-transform-parameters@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-09lMt6UsUb3/34BbECKVbVwrT9bO6lILWln237z7sLaWnMsTi7Yc9fhX5DLpkJzAGfaReXI22wP41SZmnAA3Vw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-private-methods@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-UzqRcRtWsDMTLrRWFvUBDwmw06tCQH9Rl1uAjfh6ijMSmGYQ+fpdB+cnqRC8EMh5tuuxSv0/TejGL+7vyj+50g==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-create-class-features-plugin': 7.23.7(@babel/core@7.23.7)
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-private-property-in-object@7.23.4(@babel/core@7.23.7):
+ resolution: {integrity: sha512-9G3K1YqTq3F4Vt88Djx1UZ79PDyj+yKRnUy7cZGSMe+a7jkwD259uKKuUzQlPkGam7R+8RJwh5z4xO27fA1o2A==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-annotate-as-pure': 7.22.5
+ '@babel/helper-create-class-features-plugin': 7.23.7(@babel/core@7.23.7)
+ '@babel/helper-plugin-utils': 7.22.5
+ '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.7)
+
+ /@babel/plugin-transform-property-literals@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-jR3Jn3y7cZp4oEWPFAlRsSWjxKe4PZILGBSd4nis1TsC5qeSpb+nrtihJuDhNI7QHiVbUaiXa0X2RZY3/TI6Nw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-regenerator@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-KP+75h0KghBMcVpuKisx3XTu9Ncut8Q8TuvGO4IhY+9D5DFEckQefOuIsB/gQ2tG71lCke4NMrtIPS8pOj18BQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+ regenerator-transform: 0.15.2
+
+ /@babel/plugin-transform-reserved-words@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-QnNTazY54YqgGxwIexMZva9gqbPa15t/x9VS+0fsEFWplwVpXYZivtgl43Z1vMpc1bdPP2PP8siFeVcnFvA3Cg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-shorthand-properties@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-ED2fgqZLmexWiN+YNFX26fx4gh5qHDhn1O2gvEhreLW2iI63Sqm4llRLCXALKrCnbN4Jy0VcMQZl/SAzqug/jg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-spread@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-VvfVYlrlBVu+77xVTOAoxQ6mZbnIq5FM0aGBSFEcIh03qHf+zNqA4DC/3XMUozTg7bZV3e3mZQ0i13VB6v5yUg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+ '@babel/helper-skip-transparent-expression-wrappers': 7.22.5
+
+ /@babel/plugin-transform-sticky-regex@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-HZOyN9g+rtvnOU3Yh7kSxXrKbzgrm5X4GncPY1QOquu7epga5MxKHVpYu2hvQnry/H+JjckSYRb93iNfsioAGg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-template-literals@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-Flok06AYNp7GV2oJPZZcP9vZdszev6vPBkHLwxwSpaIqx75wn6mUd3UFWsSsA0l8nXAKkyCmL/sR02m8RYGeHg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-typeof-symbol@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-4t15ViVnaFdrPC74be1gXBSMzXk3B4Us9lP7uLRQHTFpV5Dvt33pn+2MyyNxmN3VTTm3oTrZVMUmuw3oBnQ2oQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-unicode-escapes@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-OMCUx/bU6ChE3r4+ZdylEqAjaQgHAgipgW8nsCfu5pGqDcFytVd91AwRvUJSBZDz0exPGgnjoqhgRYLRjFZc9Q==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-unicode-property-regex@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-KcLIm+pDZkWZQAFJ9pdfmh89EwVfmNovFBcXko8szpBeF8z68kWIPeKlmSOkT9BXJxs2C0uk+5LxoxIv62MROA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.7)
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-unicode-regex@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-wMHpNA4x2cIA32b/ci3AfwNgheiva2W0WUKWTK7vBHBhDKfPsc5cFGNWm69WBqpwd86u1qwZ9PWevKqm1A3yAw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.7)
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/plugin-transform-unicode-sets-regex@7.23.3(@babel/core@7.23.7):
+ resolution: {integrity: sha512-W7lliA/v9bNR83Qc3q1ip9CQMZ09CcHDbHfbLRDNuAhn1Mvkr1ZNF7hPmztMQvtTGVLJ9m8IZqWsTkXOml8dbw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-create-regexp-features-plugin': 7.22.15(@babel/core@7.23.7)
+ '@babel/helper-plugin-utils': 7.22.5
+
+ /@babel/preset-env@7.23.8(@babel/core@7.23.7):
+ resolution: {integrity: sha512-lFlpmkApLkEP6woIKprO6DO60RImpatTQKtz4sUcDjVcK8M8mQ4sZsuxaTMNOZf0sqAq/ReYW1ZBHnOQwKpLWA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+ dependencies:
+ '@babel/compat-data': 7.23.5
+ '@babel/core': 7.23.7
+ '@babel/helper-compilation-targets': 7.23.6
+ '@babel/helper-plugin-utils': 7.22.5
+ '@babel/helper-validator-option': 7.23.5
+ '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.23.7(@babel/core@7.23.7)
+ '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.23.7)
+ '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.23.7)
+ '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.23.7)
+ '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.23.7)
+ '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.23.7)
+ '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.23.7)
+ '@babel/plugin-syntax-import-assertions': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-syntax-import-attributes': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.23.7)
+ '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.23.7)
+ '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.23.7)
+ '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.23.7)
+ '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.23.7)
+ '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.23.7)
+ '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.23.7)
+ '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.7)
+ '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.23.7)
+ '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.7)
+ '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.23.7)
+ '@babel/plugin-transform-arrow-functions': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-async-generator-functions': 7.23.7(@babel/core@7.23.7)
+ '@babel/plugin-transform-async-to-generator': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-block-scoped-functions': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-block-scoping': 7.23.4(@babel/core@7.23.7)
+ '@babel/plugin-transform-class-properties': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-class-static-block': 7.23.4(@babel/core@7.23.7)
+ '@babel/plugin-transform-classes': 7.23.8(@babel/core@7.23.7)
+ '@babel/plugin-transform-computed-properties': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-destructuring': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-dotall-regex': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-duplicate-keys': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-dynamic-import': 7.23.4(@babel/core@7.23.7)
+ '@babel/plugin-transform-exponentiation-operator': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-export-namespace-from': 7.23.4(@babel/core@7.23.7)
+ '@babel/plugin-transform-for-of': 7.23.6(@babel/core@7.23.7)
+ '@babel/plugin-transform-function-name': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-json-strings': 7.23.4(@babel/core@7.23.7)
+ '@babel/plugin-transform-literals': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-logical-assignment-operators': 7.23.4(@babel/core@7.23.7)
+ '@babel/plugin-transform-member-expression-literals': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-modules-amd': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-modules-systemjs': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-modules-umd': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-named-capturing-groups-regex': 7.22.5(@babel/core@7.23.7)
+ '@babel/plugin-transform-new-target': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-nullish-coalescing-operator': 7.23.4(@babel/core@7.23.7)
+ '@babel/plugin-transform-numeric-separator': 7.23.4(@babel/core@7.23.7)
+ '@babel/plugin-transform-object-rest-spread': 7.23.4(@babel/core@7.23.7)
+ '@babel/plugin-transform-object-super': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-optional-catch-binding': 7.23.4(@babel/core@7.23.7)
+ '@babel/plugin-transform-optional-chaining': 7.23.4(@babel/core@7.23.7)
+ '@babel/plugin-transform-parameters': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-private-methods': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-private-property-in-object': 7.23.4(@babel/core@7.23.7)
+ '@babel/plugin-transform-property-literals': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-regenerator': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-reserved-words': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-shorthand-properties': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-spread': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-sticky-regex': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-template-literals': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-typeof-symbol': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-unicode-escapes': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-unicode-property-regex': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-unicode-regex': 7.23.3(@babel/core@7.23.7)
+ '@babel/plugin-transform-unicode-sets-regex': 7.23.3(@babel/core@7.23.7)
+ '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.23.7)
+ babel-plugin-polyfill-corejs2: 0.4.8(@babel/core@7.23.7)
+ babel-plugin-polyfill-corejs3: 0.8.7(@babel/core@7.23.7)
+ babel-plugin-polyfill-regenerator: 0.5.5(@babel/core@7.23.7)
+ core-js-compat: 3.35.1
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ /@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.23.7):
+ 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.7
+ '@babel/helper-plugin-utils': 7.22.5
+ '@babel/types': 7.23.6
+ esutils: 2.0.3
+
+ /@babel/regjsgen@0.8.0:
+ resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==}
+
+ /@babel/runtime@7.23.8:
+ resolution: {integrity: sha512-Y7KbAP984rn1VGMbGqKmBLio9V7y5Je9GvU4rQPCPinCyNfUcToxIXl06d59URp/F3LwinvODxab5N/G6qggkw==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ regenerator-runtime: 0.14.1
+
+ /@babel/template@7.22.15:
+ resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/code-frame': 7.23.5
+ '@babel/parser': 7.23.6
+ '@babel/types': 7.23.6
+
+ /@babel/template@7.23.9:
+ resolution: {integrity: sha512-+xrD2BWLpvHKNmX2QbpdpsBaWnRxahMwJjO+KZk2JOElj5nSmKezyS1B4u+QbHMTX69t4ukm6hh9lsYQ7GHCKA==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/code-frame': 7.23.5
+ '@babel/parser': 7.23.9
+ '@babel/types': 7.23.9
+
+ /@babel/traverse@7.23.7:
+ resolution: {integrity: sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/code-frame': 7.23.5
+ '@babel/generator': 7.23.6
+ '@babel/helper-environment-visitor': 7.22.20
+ '@babel/helper-function-name': 7.23.0
+ '@babel/helper-hoist-variables': 7.22.5
+ '@babel/helper-split-export-declaration': 7.22.6
+ '@babel/parser': 7.23.6
+ '@babel/types': 7.23.6
+ debug: 4.3.4
+ globals: 11.12.0
+ transitivePeerDependencies:
+ - supports-color
+
+ /@babel/traverse@7.23.9:
+ resolution: {integrity: sha512-I/4UJ9vs90OkBtY6iiiTORVMyIhJ4kAVmsKo9KFc8UOxMeUfi2hvtIBsET5u9GizXE6/GFSuKCTNfgCswuEjRg==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/code-frame': 7.23.5
+ '@babel/generator': 7.23.6
+ '@babel/helper-environment-visitor': 7.22.20
+ '@babel/helper-function-name': 7.23.0
+ '@babel/helper-hoist-variables': 7.22.5
+ '@babel/helper-split-export-declaration': 7.22.6
+ '@babel/parser': 7.23.9
+ '@babel/types': 7.23.9
+ debug: 4.3.4
+ globals: 11.12.0
+ transitivePeerDependencies:
+ - supports-color
+
+ /@babel/types@7.23.6:
+ resolution: {integrity: sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/helper-string-parser': 7.23.4
+ '@babel/helper-validator-identifier': 7.22.20
+ to-fast-properties: 2.0.0
+
+ /@babel/types@7.23.9:
+ resolution: {integrity: sha512-dQjSq/7HaSjRM43FFGnv5keM2HsxpmyV1PfaSVm0nzzjwwTmjOe6J4bC8e3+pTEIgHaHj+1ZlLThRJ2auc/w1Q==}
+ engines: {node: '>=6.9.0'}
+ dependencies:
+ '@babel/helper-string-parser': 7.23.4
+ '@babel/helper-validator-identifier': 7.22.20
+ to-fast-properties: 2.0.0
+
+ /@braintree/sanitize-url@6.0.4:
+ resolution: {integrity: sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==}
+ dev: false
+
+ /@emotion/is-prop-valid@0.8.8:
+ resolution: {integrity: sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==}
+ requiresBuild: true
+ dependencies:
+ '@emotion/memoize': 0.7.4
+ dev: false
+ optional: true
+
+ /@emotion/memoize@0.7.4:
+ resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==}
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /@esbuild/aix-ppc64@0.19.12:
+ resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==}
+ engines: {node: '>=12'}
+ cpu: [ppc64]
+ os: [aix]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/android-arm64@0.19.12:
+ resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/android-arm@0.19.12:
+ resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/android-x64@0.19.12:
+ resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [android]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/darwin-arm64@0.19.12:
+ resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/darwin-x64@0.19.12:
+ resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/freebsd-arm64@0.19.12:
+ resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [freebsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/freebsd-x64@0.19.12:
+ resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [freebsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-arm64@0.19.12:
+ resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-arm@0.19.12:
+ resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==}
+ engines: {node: '>=12'}
+ cpu: [arm]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-ia32@0.19.12:
+ resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-loong64@0.19.12:
+ resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==}
+ engines: {node: '>=12'}
+ cpu: [loong64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-mips64el@0.19.12:
+ resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==}
+ engines: {node: '>=12'}
+ cpu: [mips64el]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-ppc64@0.19.12:
+ resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==}
+ engines: {node: '>=12'}
+ cpu: [ppc64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-riscv64@0.19.12:
+ resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==}
+ engines: {node: '>=12'}
+ cpu: [riscv64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-s390x@0.19.12:
+ resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==}
+ engines: {node: '>=12'}
+ cpu: [s390x]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/linux-x64@0.19.12:
+ resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/netbsd-x64@0.19.12:
+ resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [netbsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/openbsd-x64@0.19.12:
+ resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [openbsd]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/sunos-x64@0.19.12:
+ resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [sunos]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/win32-arm64@0.19.12:
+ resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==}
+ engines: {node: '>=12'}
+ cpu: [arm64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/win32-ia32@0.19.12:
+ resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==}
+ engines: {node: '>=12'}
+ cpu: [ia32]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@esbuild/win32-x64@0.19.12:
+ resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==}
+ engines: {node: '>=12'}
+ cpu: [x64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@eslint-community/eslint-utils@4.4.0(eslint@8.56.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.56.0
+ eslint-visitor-keys: 3.4.3
+ dev: true
+
+ /@eslint-community/regexpp@4.10.0:
+ resolution: {integrity: sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+ dev: true
+
+ /@eslint/eslintrc@2.1.4:
+ resolution: {integrity: sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==}
+ 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.24.0
+ ignore: 5.3.0
+ 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.56.0:
+ resolution: {integrity: sha512-gMsVel9D7f2HLkBma9VbtzZRehRogVRfbr++f06nL2vnCGCNlzOD+/MUov/F4p8myyAHspEhVobgjpX64q5m6A==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ dev: true
+
+ /@floating-ui/core@1.6.0:
+ resolution: {integrity: sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==}
+ dependencies:
+ '@floating-ui/utils': 0.2.1
+ dev: false
+
+ /@floating-ui/dom@1.6.1:
+ resolution: {integrity: sha512-iA8qE43/H5iGozC3W0YSnVSW42Vh522yyM1gj+BqRwVsTNOyr231PsXDaV04yT39PsO0QL2QpbI/M0ZaLUQgRQ==}
+ dependencies:
+ '@floating-ui/core': 1.6.0
+ '@floating-ui/utils': 0.2.1
+ dev: false
+
+ /@floating-ui/react-dom@2.0.8(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==}
+ peerDependencies:
+ react: '>=16.8.0'
+ react-dom: '>=16.8.0'
+ dependencies:
+ '@floating-ui/dom': 1.6.1
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@floating-ui/utils@0.2.1:
+ resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==}
+ dev: false
+
+ /@headlessui/react@1.7.18(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-4i5DOrzwN4qSgNsL4Si61VMkUcWbcSKueUV7sFhpHzQcSShdlHENE5+QBntMSRvHt8NyoFO2AGG8si9lq+w4zQ==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ react: ^16 || ^17 || ^18
+ react-dom: ^16 || ^17 || ^18
+ dependencies:
+ '@tanstack/react-virtual': 3.1.3(react-dom@18.2.0)(react@18.2.0)
+ client-only: 0.0.1
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@heroicons/react@2.1.1(react@18.2.0):
+ resolution: {integrity: sha512-JyyN9Lo66kirbCMuMMRPtJxtKJoIsXKS569ebHGGRKbl8s4CtUfLnyKJxteA+vIKySocO4s1SkTkGS4xtG/yEA==}
+ peerDependencies:
+ react: '>= 16'
+ dependencies:
+ react: 18.2.0
+ dev: false
+
+ /@hookform/resolvers@3.3.4(react-hook-form@7.50.1):
+ resolution: {integrity: sha512-o5cgpGOuJYrd+iMKvkttOclgwRW86EsWJZZRC23prf0uU2i48Htq4PuT73AVb9ionFyZrwYEITuOFGF+BydEtQ==}
+ peerDependencies:
+ react-hook-form: ^7.0.0
+ dependencies:
+ react-hook-form: 7.50.1(react@18.2.0)
+ dev: false
+
+ /@humanwhocodes/config-array@0.11.14:
+ resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==}
+ engines: {node: '>=10.10.0'}
+ dependencies:
+ '@humanwhocodes/object-schema': 2.0.2
+ 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.2:
+ resolution: {integrity: sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==}
+ dev: true
+
+ /@isaacs/cliui@8.0.2:
+ resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
+ engines: {node: '>=12'}
+ dependencies:
+ string-width: 5.1.2
+ string-width-cjs: /string-width@4.2.3
+ strip-ansi: 7.1.0
+ strip-ansi-cjs: /strip-ansi@6.0.1
+ wrap-ansi: 8.1.0
+ wrap-ansi-cjs: /wrap-ansi@7.0.0
+
+ /@jridgewell/gen-mapping@0.3.3:
+ resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==}
+ engines: {node: '>=6.0.0'}
+ dependencies:
+ '@jridgewell/set-array': 1.1.2
+ '@jridgewell/sourcemap-codec': 1.4.15
+ '@jridgewell/trace-mapping': 0.3.22
+
+ /@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==}
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.3
+ '@jridgewell/trace-mapping': 0.3.22
+
+ /@jridgewell/sourcemap-codec@1.4.15:
+ resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==}
+
+ /@jridgewell/trace-mapping@0.3.22:
+ resolution: {integrity: sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw==}
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.1
+ '@jridgewell/sourcemap-codec': 1.4.15
+
+ /@mdx-js/mdx@2.3.0:
+ resolution: {integrity: sha512-jLuwRlz8DQfQNiUCJR50Y09CGPq3fLtmtUQfVrj79E0JWu3dvsVcxVIcfhR5h0iXu+/z++zDrYeiJqifRynJkA==}
+ dependencies:
+ '@types/estree-jsx': 1.0.5
+ '@types/mdx': 2.0.11
+ estree-util-build-jsx: 2.2.2
+ estree-util-is-identifier-name: 2.1.0
+ estree-util-to-js: 1.2.0
+ estree-walker: 3.0.3
+ hast-util-to-estree: 2.3.3
+ markdown-extensions: 1.1.1
+ periscopic: 3.1.0
+ remark-mdx: 2.3.0
+ remark-parse: 10.0.2
+ remark-rehype: 10.1.0
+ unified: 10.1.2
+ unist-util-position-from-estree: 1.1.2
+ unist-util-stringify-position: 3.0.3
+ unist-util-visit: 4.1.2
+ vfile: 5.3.7
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
+ /@mdx-js/react@2.3.0(react@18.2.0):
+ resolution: {integrity: sha512-zQH//gdOmuu7nt2oJR29vFhDv88oGPmVw6BggmrHeMI+xgEkp1B2dX9/bMBSYtK0dyLX/aOmesKS09g222K1/g==}
+ peerDependencies:
+ react: '>=16'
+ dependencies:
+ '@types/mdx': 2.0.11
+ '@types/react': 18.2.48
+ react: 18.2.0
+ dev: false
+
+ /@napi-rs/simple-git-android-arm-eabi@0.1.16:
+ resolution: {integrity: sha512-dbrCL0Pl5KZG7x7tXdtVsA5CO6At5ohDX3myf5xIYn9kN4jDFxsocl8bNt6Vb/hZQoJd8fI+k5VlJt+rFhbdVw==}
+ engines: {node: '>= 10'}
+ cpu: [arm]
+ os: [android]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /@napi-rs/simple-git-android-arm64@0.1.16:
+ resolution: {integrity: sha512-xYz+TW5J09iK8SuTAKK2D5MMIsBUXVSs8nYp7HcMi8q6FCRO7yJj96YfP9PvKsc/k64hOyqGmL5DhCzY9Cu1FQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [android]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /@napi-rs/simple-git-darwin-arm64@0.1.16:
+ resolution: {integrity: sha512-XfgsYqxhUE022MJobeiX563TJqyQyX4FmYCnqrtJwAfivESVeAJiH6bQIum8dDEYMHXCsG7nL8Ok0Dp8k2m42g==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /@napi-rs/simple-git-darwin-x64@0.1.16:
+ resolution: {integrity: sha512-tkEVBhD6vgRCbeWsaAQqM3bTfpIVGeitamPPRVSbsq8qgzJ5Dx6ZedH27R7KSsA/uao7mZ3dsrNLXbu1Wy5MzA==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /@napi-rs/simple-git-linux-arm-gnueabihf@0.1.16:
+ resolution: {integrity: sha512-R6VAyNnp/yRaT7DV1Ao3r67SqTWDa+fNq2LrNy0Z8gXk2wB9ZKlrxFtLPE1WSpWknWtyRDLpRlsorh7Evk7+7w==}
+ engines: {node: '>= 10'}
+ cpu: [arm]
+ os: [linux]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /@napi-rs/simple-git-linux-arm64-gnu@0.1.16:
+ resolution: {integrity: sha512-LAGI0opFKw/HBMCV2qIBK3uWSEW9h4xd2ireZKLJy8DBPymX6NrWIamuxYNyCuACnFdPRxR4LaRFy4J5ZwuMdw==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /@napi-rs/simple-git-linux-arm64-musl@0.1.16:
+ resolution: {integrity: sha512-I57Ph0F0Yn2KW93ep+V1EzKhACqX0x49vvSiapqIsdDA2PifdEWLc1LJarBolmK7NKoPqKmf6lAKKO9lhiZzkg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /@napi-rs/simple-git-linux-x64-gnu@0.1.16:
+ resolution: {integrity: sha512-AZYYFY2V7hlcQASPEOWyOa3e1skzTct9QPzz0LiDM3f/hCFY/wBaU2M6NC5iG3d2Kr38heuyFS/+JqxLm5WaKA==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /@napi-rs/simple-git-linux-x64-musl@0.1.16:
+ resolution: {integrity: sha512-9TyMcYSBJwjT8jwjY9m24BZbu7ozyWTjsmYBYNtK3B0Um1Ov6jthSNneLVvouQ6x+k3Ow+00TiFh6bvmT00r8g==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /@napi-rs/simple-git-win32-arm64-msvc@0.1.16:
+ resolution: {integrity: sha512-uslJ1WuAHCYJWui6xjsyT47SjX6KOHDtClmNO8hqKz1pmDSNY7AjyUY8HxvD1lK9bDnWwc4JYhikS9cxCqHybw==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /@napi-rs/simple-git-win32-x64-msvc@0.1.16:
+ resolution: {integrity: sha512-SoEaVeCZCDF1MP+M9bMSXsZWgEjk4On9GWADO5JOulvzR1bKjk0s9PMHwe/YztR9F0sJzrCxwtvBZowhSJsQPg==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /@napi-rs/simple-git@0.1.16:
+ resolution: {integrity: sha512-C5wRPw9waqL2jk3jEDeJv+f7ScuO3N0a39HVdyFLkwKxHH4Sya4ZbzZsu2JLi6eEqe7RuHipHL6mC7B2OfYZZw==}
+ engines: {node: '>= 10'}
+ optionalDependencies:
+ '@napi-rs/simple-git-android-arm-eabi': 0.1.16
+ '@napi-rs/simple-git-android-arm64': 0.1.16
+ '@napi-rs/simple-git-darwin-arm64': 0.1.16
+ '@napi-rs/simple-git-darwin-x64': 0.1.16
+ '@napi-rs/simple-git-linux-arm-gnueabihf': 0.1.16
+ '@napi-rs/simple-git-linux-arm64-gnu': 0.1.16
+ '@napi-rs/simple-git-linux-arm64-musl': 0.1.16
+ '@napi-rs/simple-git-linux-x64-gnu': 0.1.16
+ '@napi-rs/simple-git-linux-x64-musl': 0.1.16
+ '@napi-rs/simple-git-win32-arm64-msvc': 0.1.16
+ '@napi-rs/simple-git-win32-x64-msvc': 0.1.16
+ dev: false
+
+ /@next-auth/prisma-adapter@1.0.7(@prisma/client@5.9.1)(next-auth@4.24.5):
+ resolution: {integrity: sha512-Cdko4KfcmKjsyHFrWwZ//lfLUbcLqlyFqjd/nYE2m3aZ7tjMNUjpks47iw7NTCnXf+5UWz5Ypyt1dSs1EP5QJw==}
+ peerDependencies:
+ '@prisma/client': '>=2.26.0 || >=3'
+ next-auth: ^4
+ dependencies:
+ '@prisma/client': 5.9.1(prisma@5.9.1)
+ next-auth: 4.24.5(next@14.1.0)(nodemailer@6.9.8)(react-dom@18.2.0)(react@18.2.0)
+ dev: false
+
+ /@next/env@13.5.6:
+ resolution: {integrity: sha512-Yac/bV5sBGkkEXmAX5FWPS9Mmo2rthrOPRQQNfycJPkjUAUclomCPH7QFVCDQ4Mp2k2K1SSM6m0zrxYrOwtFQw==}
+ dev: true
+
+ /@next/env@14.1.0:
+ resolution: {integrity: sha512-Py8zIo+02ht82brwwhTg36iogzFqGLPXlRGKQw5s+qP/kMNc4MAyDeEwBKDijk6zTIbegEgu8Qy7C1LboslQAw==}
+ dev: false
+
+ /@next/eslint-plugin-next@14.1.0:
+ resolution: {integrity: sha512-x4FavbNEeXx/baD/zC/SdrvkjSby8nBn8KcCREqk6UuwvwoAPZmaV8TFCAuo/cpovBRTIY67mHhe86MQQm/68Q==}
+ dependencies:
+ glob: 10.3.10
+ dev: true
+
+ /@next/swc-darwin-arm64@13.5.6:
+ resolution: {integrity: sha512-5nvXMzKtZfvcu4BhtV0KH1oGv4XEW+B+jOfmBdpFI3C7FrB/MfujRpWYSBBO64+qbW8pkZiSyQv9eiwnn5VIQA==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@next/swc-darwin-arm64@14.1.0:
+ resolution: {integrity: sha512-nUDn7TOGcIeyQni6lZHfzNoo9S0euXnu0jhsbMOmMJUBfgsnESdjN97kM7cBqQxZa8L/bM9om/S5/1dzCrW6wQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /@next/swc-darwin-x64@13.5.6:
+ resolution: {integrity: sha512-6cgBfxg98oOCSr4BckWjLLgiVwlL3vlLj8hXg2b+nDgm4bC/qVXXLfpLB9FHdoDu4057hzywbxKvmYGmi7yUzA==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@next/swc-darwin-x64@14.1.0:
+ resolution: {integrity: sha512-1jgudN5haWxiAl3O1ljUS2GfupPmcftu2RYJqZiMJmmbBT5M1XDffjUtRUzP4W3cBHsrvkfOFdQ71hAreNQP6g==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /@next/swc-linux-arm64-gnu@13.5.6:
+ resolution: {integrity: sha512-txagBbj1e1w47YQjcKgSU4rRVQ7uF29YpnlHV5xuVUsgCUf2FmyfJ3CPjZUvpIeXCJAoMCFAoGnbtX86BK7+sg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@next/swc-linux-arm64-gnu@14.1.0:
+ resolution: {integrity: sha512-RHo7Tcj+jllXUbK7xk2NyIDod3YcCPDZxj1WLIYxd709BQ7WuRYl3OWUNG+WUfqeQBds6kvZYlc42NJJTNi4tQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /@next/swc-linux-arm64-musl@13.5.6:
+ resolution: {integrity: sha512-cGd+H8amifT86ZldVJtAKDxUqeFyLWW+v2NlBULnLAdWsiuuN8TuhVBt8ZNpCqcAuoruoSWynvMWixTFcroq+Q==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@next/swc-linux-arm64-musl@14.1.0:
+ resolution: {integrity: sha512-v6kP8sHYxjO8RwHmWMJSq7VZP2nYCkRVQ0qolh2l6xroe9QjbgV8siTbduED4u0hlk0+tjS6/Tuy4n5XCp+l6g==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /@next/swc-linux-x64-gnu@13.5.6:
+ resolution: {integrity: sha512-Mc2b4xiIWKXIhBy2NBTwOxGD3nHLmq4keFk+d4/WL5fMsB8XdJRdtUlL87SqVCTSaf1BRuQQf1HvXZcy+rq3Nw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@next/swc-linux-x64-gnu@14.1.0:
+ resolution: {integrity: sha512-zJ2pnoFYB1F4vmEVlb/eSe+VH679zT1VdXlZKX+pE66grOgjmKJHKacf82g/sWE4MQ4Rk2FMBCRnX+l6/TVYzQ==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /@next/swc-linux-x64-musl@13.5.6:
+ resolution: {integrity: sha512-CFHvP9Qz98NruJiUnCe61O6GveKKHpJLloXbDSWRhqhkJdZD2zU5hG+gtVJR//tyW897izuHpM6Gtf6+sNgJPQ==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@next/swc-linux-x64-musl@14.1.0:
+ resolution: {integrity: sha512-rbaIYFt2X9YZBSbH/CwGAjbBG2/MrACCVu2X0+kSykHzHnYH5FjHxwXLkcoJ10cX0aWCEynpu+rP76x0914atg==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /@next/swc-win32-arm64-msvc@13.5.6:
+ resolution: {integrity: sha512-aFv1ejfkbS7PUa1qVPwzDHjQWQtknzAZWGTKYIAaS4NMtBlk3VyA6AYn593pqNanlicewqyl2jUhQAaFV/qXsg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@next/swc-win32-arm64-msvc@14.1.0:
+ resolution: {integrity: sha512-o1N5TsYc8f/HpGt39OUQpQ9AKIGApd3QLueu7hXk//2xq5Z9OxmV6sQfNp8C7qYmiOlHYODOGqNNa0e9jvchGQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /@next/swc-win32-ia32-msvc@13.5.6:
+ resolution: {integrity: sha512-XqqpHgEIlBHvzwG8sp/JXMFkLAfGLqkbVsyN+/Ih1mR8INb6YCc2x/Mbwi6hsAgUnqQztz8cvEbHJUbSl7RHDg==}
+ engines: {node: '>= 10'}
+ cpu: [ia32]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@next/swc-win32-ia32-msvc@14.1.0:
+ resolution: {integrity: sha512-XXIuB1DBRCFwNO6EEzCTMHT5pauwaSj4SWs7CYnME57eaReAKBXCnkUE80p/pAZcewm7hs+vGvNqDPacEXHVkw==}
+ engines: {node: '>= 10'}
+ cpu: [ia32]
+ os: [win32]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /@next/swc-win32-x64-msvc@13.5.6:
+ resolution: {integrity: sha512-Cqfe1YmOS7k+5mGu92nl5ULkzpKuxJrP3+4AEuPmrpFZ3BHxTY3TnHmU1On3bFmFFs6FbTcdF58CCUProGpIGQ==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
+ /@next/swc-win32-x64-msvc@14.1.0:
+ resolution: {integrity: sha512-9WEbVRRAqJ3YFVqEZIxUqkiO8l1nool1LmNxygr5HWF8AcSYsEpneUDhmjUVJEzO2A04+oPtZdombzzPPkTtgg==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /@nodelib/fs.scandir@2.1.5:
+ resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+ engines: {node: '>= 8'}
+ 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.walk@1.2.8:
+ resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+ engines: {node: '>= 8'}
+ dependencies:
+ '@nodelib/fs.scandir': 2.1.5
+ fastq: 1.16.0
+
+ /@one-ini/wasm@0.1.1:
+ resolution: {integrity: sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==}
+ dev: false
+
+ /@panva/hkdf@1.1.1:
+ resolution: {integrity: sha512-dhPeilub1NuIG0X5Kvhh9lH4iW3ZsHlnzwgwbOlgwQ2wG1IqFzsgHqmKPk3WzsdWAeaxKJxgM0+W433RmN45GA==}
+ dev: false
+
+ /@pkgjs/parseargs@0.11.0:
+ resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
+ engines: {node: '>=14'}
+ requiresBuild: true
+ optional: true
+
+ /@prisma/client@5.9.1(prisma@5.9.1):
+ resolution: {integrity: sha512-caSOnG4kxcSkhqC/2ShV7rEoWwd3XrftokxJqOCMVvia4NYV/TPtJlS9C2os3Igxw/Qyxumj9GBQzcStzECvtQ==}
+ engines: {node: '>=16.13'}
+ requiresBuild: true
+ peerDependencies:
+ prisma: '*'
+ peerDependenciesMeta:
+ prisma:
+ optional: true
+ dependencies:
+ prisma: 5.9.1
+ dev: false
+
+ /@prisma/debug@5.9.1:
+ resolution: {integrity: sha512-yAHFSFCg8KVoL0oRUno3m60GAjsUKYUDkQ+9BA2X2JfVR3kRVSJFc/GpQ2fSORi4pSHZR9orfM4UC9OVXIFFTA==}
+
+ /@prisma/engines-version@5.9.0-32.23fdc5965b1e05fc54e5f26ed3de66776b93de64:
+ resolution: {integrity: sha512-HFl7275yF0FWbdcNvcSRbbu9JCBSLMcurYwvWc8WGDnpu7APxQo2ONtZrUggU3WxLxUJ2uBX+0GOFIcJeVeOOQ==}
+
+ /@prisma/engines@5.9.1:
+ resolution: {integrity: sha512-gkdXmjxQ5jktxWNdDA5aZZ6R8rH74JkoKq6LD5mACSvxd2vbqWeWIOV0Py5wFC8vofOYShbt6XUeCIUmrOzOnQ==}
+ requiresBuild: true
+ dependencies:
+ '@prisma/debug': 5.9.1
+ '@prisma/engines-version': 5.9.0-32.23fdc5965b1e05fc54e5f26ed3de66776b93de64
+ '@prisma/fetch-engine': 5.9.1
+ '@prisma/get-platform': 5.9.1
+
+ /@prisma/fetch-engine@5.9.1:
+ resolution: {integrity: sha512-l0goQOMcNVOJs1kAcwqpKq3ylvkD9F04Ioe1oJoCqmz05mw22bNAKKGWuDd3zTUoUZr97va0c/UfLNru+PDmNA==}
+ dependencies:
+ '@prisma/debug': 5.9.1
+ '@prisma/engines-version': 5.9.0-32.23fdc5965b1e05fc54e5f26ed3de66776b93de64
+ '@prisma/get-platform': 5.9.1
+
+ /@prisma/get-platform@5.9.1:
+ resolution: {integrity: sha512-6OQsNxTyhvG+T2Ksr8FPFpuPeL4r9u0JF0OZHUBI/Uy9SS43sPyAIutt4ZEAyqWQt104ERh70EZedkHZKsnNbg==}
+ dependencies:
+ '@prisma/debug': 5.9.1
+
+ /@radix-ui/primitive@1.0.0:
+ resolution: {integrity: sha512-3e7rn8FDMin4CgeL7Z/49smCA3rFYY3Ha2rUQ7HRWFadS5iCRw08ZgVT1LaNTCNqgvrUiyczLflrVrF0SRQtNA==}
+ dependencies:
+ '@babel/runtime': 7.23.8
+ dev: false
+
+ /@radix-ui/primitive@1.0.1:
+ resolution: {integrity: sha512-yQ8oGX2GVsEYMWGxcovu1uGWPCxV5BFfeeYxqPmuAzUyLT9qmaMXSAhXpb0WrspIeqYzdJpkh2vHModJPgRIaw==}
+ dependencies:
+ '@babel/runtime': 7.23.8
+ dev: false
+
+ /@radix-ui/react-alert-dialog@1.0.5(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-OrVIOcZL0tl6xibeuGt5/+UxoT2N27KCFOPjFyfXMnchxSHZ/OW7cCX2nGlIYJrbHK/fczPcFzAwvNBB6XBNMA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/primitive': 1.0.1
+ '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-dialog': 1.0.5(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-slot': 1.0.2(@types/react@18.2.48)(react@18.2.0)
+ '@types/react': 18.2.48
+ '@types/react-dom': 18.2.18
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@radix-ui/react-arrow@1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-wSP+pHsB/jQRaL6voubsQ/ZlrGBHHrOjmBnr19hxYgtS0WvAFwZhK2WP/YY5yF9uKECCEEDGxuLxq1NBK51wFA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@types/react': 18.2.48
+ '@types/react-dom': 18.2.18
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@radix-ui/react-avatar@1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-kVK2K7ZD3wwj3qhle0ElXhOjbezIgyl2hVvgwfIdexL3rN6zJmy5AqqIf+D31lxVppdzV8CjAfZ6PklkmInZLw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@types/react': 18.2.48
+ '@types/react-dom': 18.2.18
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@radix-ui/react-checkbox@1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-CBuGQa52aAYnADZVt/KBQzXrwx6TqnlwtcIPGtVt5JkkzQwMOLJjPukimhfKEr4GQNd43C+djUh5Ikopj8pSLg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/primitive': 1.0.1
+ '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-use-previous': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@types/react': 18.2.48
+ '@types/react-dom': 18.2.18
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@radix-ui/react-collection@1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-3SzW+0PW7yBBoQlT8wNcGtaxaD0XSu0uLUFgrtHY08Acx05TaHaOmVLR73c0j/cqpDy53KBMO7s0dx2wmOIDIA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-slot': 1.0.2(@types/react@18.2.48)(react@18.2.0)
+ '@types/react': 18.2.48
+ '@types/react-dom': 18.2.18
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@radix-ui/react-compose-refs@1.0.0(react@18.2.0):
+ resolution: {integrity: sha512-0KaSv6sx787/hK3eF53iOkiSLwAGlFMx5lotrqD2pTjB18KbybKoEIgkNZTKC60YECDQTKGTRcDBILwZVqVKvA==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0
+ dependencies:
+ '@babel/runtime': 7.23.8
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.48)(react@18.2.0):
+ resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@types/react': 18.2.48
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/react-context@1.0.0(react@18.2.0):
+ resolution: {integrity: sha512-1pVM9RfOQ+n/N5PJK33kRSKsr1glNxomxONs5c49MliinBY6Yw2Q995qfBUUo0/Mbg05B/sGA0gkgPI7kmSHBg==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0
+ dependencies:
+ '@babel/runtime': 7.23.8
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/react-context@1.0.1(@types/react@18.2.48)(react@18.2.0):
+ resolution: {integrity: sha512-ebbrdFoYTcuZ0v4wG5tedGnp9tzcV8awzsxYph7gXUyvnNLuTIcCk1q17JEbnVhXAKG9oX3KtchwiMIAYp9NLg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@types/react': 18.2.48
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/react-dialog@1.0.0(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-Yn9YU+QlHYLWwV1XfKiqnGVpWYWk6MeBVM6x/bcoyPvxgjQGoeT35482viLPctTMWoMw0PoHgqfSox7Ig+957Q==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/primitive': 1.0.0
+ '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0)
+ '@radix-ui/react-context': 1.0.0(react@18.2.0)
+ '@radix-ui/react-dismissable-layer': 1.0.0(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-focus-guards': 1.0.0(react@18.2.0)
+ '@radix-ui/react-focus-scope': 1.0.0(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-id': 1.0.0(react@18.2.0)
+ '@radix-ui/react-portal': 1.0.0(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-presence': 1.0.0(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-primitive': 1.0.0(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-slot': 1.0.0(react@18.2.0)
+ '@radix-ui/react-use-controllable-state': 1.0.0(react@18.2.0)
+ aria-hidden: 1.2.3
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ react-remove-scroll: 2.5.4(@types/react@18.2.48)(react@18.2.0)
+ transitivePeerDependencies:
+ - '@types/react'
+ dev: false
+
+ /@radix-ui/react-dialog@1.0.5(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-GjWJX/AUpB703eEBanuBnIWdIXg6NvJFCXcNlSZk4xdszCdhrJgBoUd1cGk67vFO+WdA2pfI/plOpqz/5GUP6Q==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/primitive': 1.0.1
+ '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-id': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-slot': 1.0.2(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@types/react': 18.2.48
+ '@types/react-dom': 18.2.18
+ aria-hidden: 1.2.3
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ react-remove-scroll: 2.5.5(@types/react@18.2.48)(react@18.2.0)
+ dev: false
+
+ /@radix-ui/react-direction@1.0.1(@types/react@18.2.48)(react@18.2.0):
+ resolution: {integrity: sha512-RXcvnXgyvYvBEOhCBuddKecVkoMiI10Jcm5cTI7abJRAHYfFxeu+FBQs/DvdxSYucxR5mna0dNsL6QFlds5TMA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@types/react': 18.2.48
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/react-dismissable-layer@1.0.0(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-n7kDRfx+LB1zLueRDvZ1Pd0bxdJWDUZNQ/GWoxDn2prnuJKRdxsjulejX/ePkOsLi2tTm6P24mDqlMSgQpsT6g==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/primitive': 1.0.0
+ '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0)
+ '@radix-ui/react-primitive': 1.0.0(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0)
+ '@radix-ui/react-use-escape-keydown': 1.0.0(react@18.2.0)
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@radix-ui/react-dismissable-layer@1.0.5(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-aJeDjQhywg9LBu2t/At58hCvr7pEm0o2Ke1x33B+MhjNmmZ17sy4KImo0KPLgsnc/zN7GPdce8Cnn0SWvwZO7g==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/primitive': 1.0.1
+ '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-use-escape-keydown': 1.0.3(@types/react@18.2.48)(react@18.2.0)
+ '@types/react': 18.2.48
+ '@types/react-dom': 18.2.18
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@radix-ui/react-focus-guards@1.0.0(react@18.2.0):
+ resolution: {integrity: sha512-UagjDk4ijOAnGu4WMUPj9ahi7/zJJqNZ9ZAiGPp7waUWJO0O1aWXi/udPphI0IUjvrhBsZJGSN66dR2dsueLWQ==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0
+ dependencies:
+ '@babel/runtime': 7.23.8
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/react-focus-guards@1.0.1(@types/react@18.2.48)(react@18.2.0):
+ resolution: {integrity: sha512-Rect2dWbQ8waGzhMavsIbmSVCgYxkXLxxR3ZvCX79JOglzdEy4JXMb98lq4hPxUbLr77nP0UOGf4rcMU+s1pUA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@types/react': 18.2.48
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/react-focus-scope@1.0.0(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-C4SWtsULLGf/2L4oGeIHlvWQx7Rf+7cX/vKOAD2dXW0A1b5QXwi3wWeaEgW+wn+SEVrraMUk05vLU9fZZz5HbQ==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0)
+ '@radix-ui/react-primitive': 1.0.0(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0)
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@radix-ui/react-focus-scope@1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-sL04Mgvf+FmyvZeYfNu1EPAaaxD+aw7cYeIB9L9Fvq8+urhltTRaEo5ysKOpHuKPclsZcSUMKlN05x4u+CINpA==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@types/react': 18.2.48
+ '@types/react-dom': 18.2.18
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@radix-ui/react-id@1.0.0(react@18.2.0):
+ resolution: {integrity: sha512-Q6iAB/U7Tq3NTolBBQbHTgclPmGWE3OlktGGqrClPozSw4vkQ1DfQAOtzgRPecKsMdJINE05iaoDUG8tRzCBjw==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/react-use-layout-effect': 1.0.0(react@18.2.0)
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/react-id@1.0.1(@types/react@18.2.48)(react@18.2.0):
+ resolution: {integrity: sha512-tI7sT/kqYp8p96yGWY1OAnLHrqDgzHefRBKQ2YAkBS5ja7QLcZ9Z/uY7bEjPUatf8RomoXM8/1sMj1IJaE5UzQ==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@types/react': 18.2.48
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/react-label@2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-N5ehvlM7qoTLx7nWPodsPYPgMzA5WM8zZChQg8nyFJKnDO5WHdba1vv5/H6IO5LtJMfD2Q3wh1qHFGNtK0w3bQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@types/react': 18.2.48
+ '@types/react-dom': 18.2.18
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@radix-ui/react-popover@1.0.7(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-shtvVnlsxT6faMnK/a7n0wptwBD23xc1Z5mdrtKLwVEfsEMXodS0r5s0/g5P0hX//EKYZS2sxUjqfzlg52ZSnQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/primitive': 1.0.1
+ '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-dismissable-layer': 1.0.5(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-focus-guards': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-focus-scope': 1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-id': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-popper': 1.1.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-portal': 1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-slot': 1.0.2(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@types/react': 18.2.48
+ '@types/react-dom': 18.2.18
+ aria-hidden: 1.2.3
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ react-remove-scroll: 2.5.5(@types/react@18.2.48)(react@18.2.0)
+ dev: false
+
+ /@radix-ui/react-popper@1.1.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-cKpopj/5RHZWjrbF2846jBNacjQVwkP068DfmgrNJXpvVWrOvlAmE9xSiy5OqeE+Gi8D9fP+oDhUnPqNMY8/5w==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@floating-ui/react-dom': 2.0.8(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-arrow': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-use-rect': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-use-size': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/rect': 1.0.1
+ '@types/react': 18.2.48
+ '@types/react-dom': 18.2.18
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@radix-ui/react-portal@1.0.0(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-a8qyFO/Xb99d8wQdu4o7qnigNjTPG123uADNecz0eX4usnQEj7o+cG4ZX4zkqq98NYekT7UoEQIjxBNWIFuqTA==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/react-primitive': 1.0.0(react-dom@18.2.0)(react@18.2.0)
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@radix-ui/react-portal@1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@types/react': 18.2.48
+ '@types/react-dom': 18.2.18
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@radix-ui/react-presence@1.0.0(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-A+6XEvN01NfVWiKu38ybawfHsBjWum42MRPnEuqPsBZ4eV7e/7K321B5VgYMPv3Xx5An6o1/l9ZuDBgmcmWK3w==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0)
+ '@radix-ui/react-use-layout-effect': 1.0.0(react@18.2.0)
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@radix-ui/react-presence@1.0.1(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-UXLW4UAbIY5ZjcvzjfRFo5gxva8QirC9hF7wRE4U5gz+TP0DbRk+//qyuAQ1McDxBt1xNMBTaciFGvEmJvAZCg==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@types/react': 18.2.48
+ '@types/react-dom': 18.2.18
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@radix-ui/react-primitive@1.0.0(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-EyXe6mnRlHZ8b6f4ilTDrXmkLShICIuOTTj0GX4w1rp+wSxf3+TD05u1UOITC8VsJ2a9nwHvdXtOXEOl0Cw/zQ==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/react-slot': 1.0.0(react@18.2.0)
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@radix-ui/react-primitive@1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/react-slot': 1.0.2(@types/react@18.2.48)(react@18.2.0)
+ '@types/react': 18.2.48
+ '@types/react-dom': 18.2.18
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@radix-ui/react-roving-focus@1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-2mUg5Mgcu001VkGy+FfzZyzbmuUWzgWkj3rvv4yu+mLw03+mTzbxZHvfcGyFp2b8EkQeMkpRQ5FiA2Vr2O6TeQ==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/primitive': 1.0.1
+ '@radix-ui/react-collection': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-direction': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-id': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@types/react': 18.2.48
+ '@types/react-dom': 18.2.18
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@radix-ui/react-separator@1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-itYmTy/kokS21aiV5+Z56MZB54KrhPgn6eHDKkFeOLR34HMN2s8PaN47qZZAGnvupcjxHaFZnW4pQEh0BvvVuw==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@types/react': 18.2.48
+ '@types/react-dom': 18.2.18
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@radix-ui/react-slot@1.0.0(react@18.2.0):
+ resolution: {integrity: sha512-3mrKauI/tWXo1Ll+gN5dHcxDPdm/Df1ufcDLCecn+pnCIVcdWE7CujXo8QaXOWRJyZyQWWbpB8eFwHzWXlv5mQ==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0)
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/react-slot@1.0.2(@types/react@18.2.48)(react@18.2.0):
+ resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@types/react': 18.2.48
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/react-tabs@1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-egZfYY/+wRNCflXNHx+dePvnz9FbmssDTJBtgRfDY7e8SE5oIo3Py2eCB1ckAbh1Q7cQ/6yJZThJ++sgbxibog==}
+ peerDependencies:
+ '@types/react': '*'
+ '@types/react-dom': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/primitive': 1.0.1
+ '@radix-ui/react-context': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-direction': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-id': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@radix-ui/react-presence': 1.0.1(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-primitive': 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-roving-focus': 1.0.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ '@radix-ui/react-use-controllable-state': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@types/react': 18.2.48
+ '@types/react-dom': 18.2.18
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@radix-ui/react-use-callback-ref@1.0.0(react@18.2.0):
+ resolution: {integrity: sha512-GZtyzoHz95Rhs6S63D2t/eqvdFCm7I+yHMLVQheKM7nBD8mbZIt+ct1jz4536MDnaOGKIxynJ8eHTkVGVVkoTg==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0
+ dependencies:
+ '@babel/runtime': 7.23.8
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/react-use-callback-ref@1.0.1(@types/react@18.2.48)(react@18.2.0):
+ resolution: {integrity: sha512-D94LjX4Sp0xJFVaoQOd3OO9k7tpBYNOXdVhkltUbGv2Qb9OXdrg/CpsjlZv7ia14Sylv398LswWBVVu5nqKzAQ==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@types/react': 18.2.48
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/react-use-controllable-state@1.0.0(react@18.2.0):
+ resolution: {integrity: sha512-FohDoZvk3mEXh9AWAVyRTYR4Sq7/gavuofglmiXB2g1aKyboUD4YtgWxKj8O5n+Uak52gXQ4wKz5IFST4vtJHg==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0)
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/react-use-controllable-state@1.0.1(@types/react@18.2.48)(react@18.2.0):
+ resolution: {integrity: sha512-Svl5GY5FQeN758fWKrjM6Qb7asvXeiZltlT4U2gVfl8Gx5UAv2sMR0LWo8yhsIZh2oQ0eFdZ59aoOOMV7b47VA==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@types/react': 18.2.48
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/react-use-escape-keydown@1.0.0(react@18.2.0):
+ resolution: {integrity: sha512-JwfBCUIfhXRxKExgIqGa4CQsiMemo1Xt0W/B4ei3fpzpvPENKpMKQ8mZSB6Acj3ebrAEgi2xiQvcI1PAAodvyg==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/react-use-callback-ref': 1.0.0(react@18.2.0)
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/react-use-escape-keydown@1.0.3(@types/react@18.2.48)(react@18.2.0):
+ resolution: {integrity: sha512-vyL82j40hcFicA+M4Ex7hVkB9vHgSse1ZWomAqV2Je3RleKGO5iM8KMOEtfoSB0PnIelMd2lATjTGMYqN5ylTg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/react-use-callback-ref': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@types/react': 18.2.48
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/react-use-layout-effect@1.0.0(react@18.2.0):
+ resolution: {integrity: sha512-6Tpkq+R6LOlmQb1R5NNETLG0B4YP0wc+klfXafpUCj6JGyaUc8il7/kUZ7m59rGbXGczE9Bs+iz2qloqsZBduQ==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0
+ dependencies:
+ '@babel/runtime': 7.23.8
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/react-use-layout-effect@1.0.1(@types/react@18.2.48)(react@18.2.0):
+ resolution: {integrity: sha512-v/5RegiJWYdoCvMnITBkNNx6bCj20fiaJnWtRkU18yITptraXjffz5Qbn05uOiQnOvi+dbkznkoaMltz1GnszQ==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@types/react': 18.2.48
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/react-use-previous@1.0.1(@types/react@18.2.48)(react@18.2.0):
+ resolution: {integrity: sha512-cV5La9DPwiQ7S0gf/0qiD6YgNqM5Fk97Kdrlc5yBcrF3jyEZQwm7vYFqMo4IfeHgJXsRaMvLABFtd0OVEmZhDw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@types/react': 18.2.48
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/react-use-rect@1.0.1(@types/react@18.2.48)(react@18.2.0):
+ resolution: {integrity: sha512-Cq5DLuSiuYVKNU8orzJMbl15TXilTnJKUCltMVQg53BQOF1/C5toAaGrowkgksdBQ9H+SRL23g0HDmg9tvmxXw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/rect': 1.0.1
+ '@types/react': 18.2.48
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/react-use-size@1.0.1(@types/react@18.2.48)(react@18.2.0):
+ resolution: {integrity: sha512-ibay+VqrgcaI6veAojjofPATwledXiSmX+C0KrBk/xgpX9rBzPV3OsfwlhQdUOFbh+LKQorLYT+xTXW9V8yd0g==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@radix-ui/react-use-layout-effect': 1.0.1(@types/react@18.2.48)(react@18.2.0)
+ '@types/react': 18.2.48
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/rect@1.0.1:
+ resolution: {integrity: sha512-fyrgCaedtvMg9NK3en0pnOYJdtfwxUcNolezkNPUsoX57X8oQk+NkqcvzHXD2uKNij6GXmWU9NDru2IWjrO4BQ==}
+ dependencies:
+ '@babel/runtime': 7.23.8
+ dev: false
+
+ /@react-email/render@0.0.12:
+ resolution: {integrity: sha512-S8WRv/PqECEi6x0QJBj0asnAb5GFtJaHlnByxLETLkgJjc76cxMYDH4r9wdbuJ4sjkcbpwP3LPnVzwS+aIjT7g==}
+ engines: {node: '>=18.0.0'}
+ dependencies:
+ html-to-text: 9.0.5
+ js-beautify: 1.15.1
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@rollup/plugin-babel@5.3.1(@babel/core@7.23.7)(rollup@2.79.1):
+ resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==}
+ engines: {node: '>= 10.0.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+ '@types/babel__core': ^7.1.9
+ rollup: ^1.20.0||^2.0.0
+ peerDependenciesMeta:
+ '@types/babel__core':
+ optional: true
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-module-imports': 7.22.15
+ '@rollup/pluginutils': 3.1.0(rollup@2.79.1)
+ rollup: 2.79.1
+
+ /@rollup/plugin-node-resolve@11.2.1(rollup@2.79.1):
+ resolution: {integrity: sha512-yc2n43jcqVyGE2sqV5/YCmocy9ArjVAP/BeXyTtADTBBX6V0e5UMqwO8CdQ0kzjb6zu5P1qMzsScCMRvE9OlVg==}
+ engines: {node: '>= 10.0.0'}
+ peerDependencies:
+ rollup: ^1.20.0||^2.0.0
+ dependencies:
+ '@rollup/pluginutils': 3.1.0(rollup@2.79.1)
+ '@types/resolve': 1.17.1
+ builtin-modules: 3.3.0
+ deepmerge: 4.3.1
+ is-module: 1.0.0
+ resolve: 1.22.8
+ rollup: 2.79.1
+
+ /@rollup/plugin-replace@2.4.2(rollup@2.79.1):
+ resolution: {integrity: sha512-IGcu+cydlUMZ5En85jxHH4qj2hta/11BHq95iHEyb2sbgiN0eCdzvUcHw5gt9pBL5lTi4JDYJ1acCoMGpTvEZg==}
+ peerDependencies:
+ rollup: ^1.20.0 || ^2.0.0
+ dependencies:
+ '@rollup/pluginutils': 3.1.0(rollup@2.79.1)
+ magic-string: 0.25.9
+ rollup: 2.79.1
+
+ /@rollup/pluginutils@3.1.0(rollup@2.79.1):
+ resolution: {integrity: sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==}
+ engines: {node: '>= 8.0.0'}
+ peerDependencies:
+ rollup: ^1.20.0||^2.0.0
+ dependencies:
+ '@types/estree': 0.0.39
+ estree-walker: 1.0.1
+ picomatch: 2.3.1
+ rollup: 2.79.1
+
+ /@rushstack/eslint-patch@1.7.0:
+ resolution: {integrity: sha512-Jh4t/593gxs0lJZ/z3NnasKlplXT2f+4y/LZYuaKZW5KAaiVFL/fThhs+17EbUd53jUVJ0QudYCBGbN/psvaqg==}
+ dev: true
+
+ /@selderee/plugin-htmlparser2@0.11.0:
+ resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==}
+ dependencies:
+ domhandler: 5.0.3
+ selderee: 0.11.0
+ dev: false
+
+ /@smithy/abort-controller@2.1.1:
+ resolution: {integrity: sha512-1+qdrUqLhaALYL0iOcN43EP6yAXXQ2wWZ6taf4S2pNGowmOc5gx+iMQv+E42JizNJjB0+gEadOXeV1Bf7JWL1Q==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/chunked-blob-reader-native@2.1.1:
+ resolution: {integrity: sha512-zNW+43dltfNMUrBEYLMWgI8lQr0uhtTcUyxkgC9EP4j17WREzgSFMPUFVrVV6Rc2+QtWERYjb4tzZnQGa7R9fQ==}
+ dependencies:
+ '@smithy/util-base64': 2.1.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/chunked-blob-reader@2.1.1:
+ resolution: {integrity: sha512-NjNFCKxC4jVvn+lUr3Yo4/PmUJj3tbyqH6GNHueyTGS5Q27vlEJ1MkNhUDV8QGxJI7Bodnc2pD18lU2zRfhHlQ==}
+ dependencies:
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/config-resolver@2.1.1:
+ resolution: {integrity: sha512-lxfLDpZm+AWAHPFZps5JfDoO9Ux1764fOgvRUBpHIO8HWHcSN1dkgsago1qLRVgm1BZ8RCm8cgv99QvtaOWIhw==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/node-config-provider': 2.2.1
+ '@smithy/types': 2.9.1
+ '@smithy/util-config-provider': 2.2.1
+ '@smithy/util-middleware': 2.1.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/core@1.3.2:
+ resolution: {integrity: sha512-tYDmTp0f2TZVE18jAOH1PnmkngLQ+dOGUlMd1u67s87ieueNeyqhja6z/Z4MxhybEiXKOWFOmGjfTZWFxljwJw==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/middleware-endpoint': 2.4.1
+ '@smithy/middleware-retry': 2.1.1
+ '@smithy/middleware-serde': 2.1.1
+ '@smithy/protocol-http': 3.1.1
+ '@smithy/smithy-client': 2.3.1
+ '@smithy/types': 2.9.1
+ '@smithy/util-middleware': 2.1.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/credential-provider-imds@2.2.1:
+ resolution: {integrity: sha512-7XHjZUxmZYnONheVQL7j5zvZXga+EWNgwEAP6OPZTi7l8J4JTeNh9aIOfE5fKHZ/ee2IeNOh54ZrSna+Vc6TFA==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/node-config-provider': 2.2.1
+ '@smithy/property-provider': 2.1.1
+ '@smithy/types': 2.9.1
+ '@smithy/url-parser': 2.1.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/eventstream-codec@2.1.1:
+ resolution: {integrity: sha512-E8KYBxBIuU4c+zrpR22VsVrOPoEDzk35bQR3E+xm4k6Pa6JqzkDOdMyf9Atac5GPNKHJBdVaQ4JtjdWX2rl/nw==}
+ dependencies:
+ '@aws-crypto/crc32': 3.0.0
+ '@smithy/types': 2.9.1
+ '@smithy/util-hex-encoding': 2.1.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/eventstream-serde-browser@2.1.1:
+ resolution: {integrity: sha512-JvEdCmGlZUay5VtlT8/kdR6FlvqTDUiJecMjXsBb0+k1H/qc9ME5n2XKPo8q/MZwEIA1GmGgYMokKGjVvMiDow==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/eventstream-serde-universal': 2.1.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/eventstream-serde-config-resolver@2.1.1:
+ resolution: {integrity: sha512-EqNqXYp3+dk//NmW3NAgQr9bEQ7fsu/CcxQmTiq07JlaIcne/CBWpMZETyXm9w5LXkhduBsdXdlMscfDUDn2fA==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/eventstream-serde-node@2.1.1:
+ resolution: {integrity: sha512-LF882q/aFidFNDX7uROAGxq3H0B7rjyPkV6QDn6/KDQ+CG7AFkRccjxRf1xqajq/Pe4bMGGr+VKAaoF6lELIQw==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/eventstream-serde-universal': 2.1.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/eventstream-serde-universal@2.1.1:
+ resolution: {integrity: sha512-LR0mMT+XIYTxk4k2fIxEA1BPtW3685QlqufUEUAX1AJcfFfxNDKEvuCRZbO8ntJb10DrIFVJR9vb0MhDCi0sAQ==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/eventstream-codec': 2.1.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/fetch-http-handler@2.4.1:
+ resolution: {integrity: sha512-VYGLinPsFqH68lxfRhjQaSkjXM7JysUOJDTNjHBuN/ykyRb2f1gyavN9+VhhPTWCy32L4yZ2fdhpCs/nStEicg==}
+ dependencies:
+ '@smithy/protocol-http': 3.1.1
+ '@smithy/querystring-builder': 2.1.1
+ '@smithy/types': 2.9.1
+ '@smithy/util-base64': 2.1.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/hash-blob-browser@2.1.1:
+ resolution: {integrity: sha512-jizu1+2PAUjiGIfRtlPEU8Yo6zn+d78ti/ZHDesdf1SUn2BuZW433JlPoCOLH3dBoEEvTgLvQ8tUGSoTTALA+A==}
+ dependencies:
+ '@smithy/chunked-blob-reader': 2.1.1
+ '@smithy/chunked-blob-reader-native': 2.1.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/hash-node@2.1.1:
+ resolution: {integrity: sha512-Qhoq0N8f2OtCnvUpCf+g1vSyhYQrZjhSwvJ9qvR8BUGOtTXiyv2x1OD2e6jVGmlpC4E4ax1USHoyGfV9JFsACg==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/types': 2.9.1
+ '@smithy/util-buffer-from': 2.1.1
+ '@smithy/util-utf8': 2.1.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/hash-stream-node@2.1.1:
+ resolution: {integrity: sha512-VgDaKcfCy0iHcmtAZgZ3Yw9g37Gkn2JsQiMtFQXUh8Wmo3GfNgDwLOtdhJ272pOT7DStzpe9cNr+eV5Au8KfQA==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/types': 2.9.1
+ '@smithy/util-utf8': 2.1.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/invalid-dependency@2.1.1:
+ resolution: {integrity: sha512-7WTgnKw+VPg8fxu2v9AlNOQ5yaz6RA54zOVB4f6vQuR0xFKd+RzlCpt0WidYTsye7F+FYDIaS/RnJW4pxjNInw==}
+ dependencies:
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/is-array-buffer@2.1.1:
+ resolution: {integrity: sha512-xozSQrcUinPpNPNPds4S7z/FakDTh1MZWtRP/2vQtYB/u3HYrX2UXuZs+VhaKBd6Vc7g2XPr2ZtwGBNDN6fNKQ==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/md5-js@2.1.1:
+ resolution: {integrity: sha512-L3MbIYBIdLlT+MWTYrdVSv/dow1+6iZ1Ad7xS0OHxTTs17d753ZcpOV4Ro7M7tRAVWML/sg2IAp/zzCb6aAttg==}
+ dependencies:
+ '@smithy/types': 2.9.1
+ '@smithy/util-utf8': 2.1.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/middleware-content-length@2.1.1:
+ resolution: {integrity: sha512-rSr9ezUl9qMgiJR0UVtVOGEZElMdGFyl8FzWEF5iEKTlcWxGr2wTqGfDwtH3LAB7h+FPkxqv4ZU4cpuCN9Kf/g==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/protocol-http': 3.1.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/middleware-endpoint@2.4.1:
+ resolution: {integrity: sha512-XPZTb1E2Oav60Ven3n2PFx+rX9EDsU/jSTA8VDamt7FXks67ekjPY/XrmmPDQaFJOTUHJNKjd8+kZxVO5Ael4Q==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/middleware-serde': 2.1.1
+ '@smithy/node-config-provider': 2.2.1
+ '@smithy/shared-ini-file-loader': 2.3.1
+ '@smithy/types': 2.9.1
+ '@smithy/url-parser': 2.1.1
+ '@smithy/util-middleware': 2.1.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/middleware-retry@2.1.1:
+ resolution: {integrity: sha512-eMIHOBTXro6JZ+WWzZWd/8fS8ht5nS5KDQjzhNMHNRcG5FkNTqcKpYhw7TETMYzbLfhO5FYghHy1vqDWM4FLDA==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/node-config-provider': 2.2.1
+ '@smithy/protocol-http': 3.1.1
+ '@smithy/service-error-classification': 2.1.1
+ '@smithy/smithy-client': 2.3.1
+ '@smithy/types': 2.9.1
+ '@smithy/util-middleware': 2.1.1
+ '@smithy/util-retry': 2.1.1
+ tslib: 2.6.2
+ uuid: 8.3.2
+ dev: false
+
+ /@smithy/middleware-serde@2.1.1:
+ resolution: {integrity: sha512-D8Gq0aQBeE1pxf3cjWVkRr2W54t+cdM2zx78tNrVhqrDykRA7asq8yVJij1u5NDtKzKqzBSPYh7iW0svUKg76g==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/middleware-stack@2.1.1:
+ resolution: {integrity: sha512-KPJhRlhsl8CjgGXK/DoDcrFGfAqoqvuwlbxy+uOO4g2Azn1dhH+GVfC3RAp+6PoL5PWPb+vt6Z23FP+Mr6qeCw==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/node-config-provider@2.2.1:
+ resolution: {integrity: sha512-epzK3x1xNxA9oJgHQ5nz+2j6DsJKdHfieb+YgJ7ATWxzNcB7Hc+Uya2TUck5MicOPhDV8HZImND7ZOecVr+OWg==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/property-provider': 2.1.1
+ '@smithy/shared-ini-file-loader': 2.3.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/node-http-handler@2.3.1:
+ resolution: {integrity: sha512-gLA8qK2nL9J0Rk/WEZSvgin4AppvuCYRYg61dcUo/uKxvMZsMInL5I5ZdJTogOvdfVug3N2dgI5ffcUfS4S9PA==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/abort-controller': 2.1.1
+ '@smithy/protocol-http': 3.1.1
+ '@smithy/querystring-builder': 2.1.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/property-provider@2.1.1:
+ resolution: {integrity: sha512-FX7JhhD/o5HwSwg6GLK9zxrMUrGnb3PzNBrcthqHKBc3dH0UfgEAU24xnJ8F0uow5mj17UeBEOI6o3CF2k7Mhw==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/protocol-http@3.1.1:
+ resolution: {integrity: sha512-6ZRTSsaXuSL9++qEwH851hJjUA0OgXdQFCs+VDw4tGH256jQ3TjYY/i34N4vd24RV3nrjNsgd1yhb57uMoKbzQ==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/querystring-builder@2.1.1:
+ resolution: {integrity: sha512-C/ko/CeEa8jdYE4gt6nHO5XDrlSJ3vdCG0ZAc6nD5ZIE7LBp0jCx4qoqp7eoutBu7VrGMXERSRoPqwi1WjCPbg==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/types': 2.9.1
+ '@smithy/util-uri-escape': 2.1.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/querystring-parser@2.1.1:
+ resolution: {integrity: sha512-H4+6jKGVhG1W4CIxfBaSsbm98lOO88tpDWmZLgkJpt8Zkk/+uG0FmmqMuCAc3HNM2ZDV+JbErxr0l5BcuIf/XQ==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/service-error-classification@2.1.1:
+ resolution: {integrity: sha512-txEdZxPUgM1PwGvDvHzqhXisrc5LlRWYCf2yyHfvITWioAKat7srQvpjMAvgzf0t6t7j8yHrryXU9xt7RZqFpw==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/types': 2.9.1
+ dev: false
+
+ /@smithy/shared-ini-file-loader@2.3.1:
+ resolution: {integrity: sha512-2E2kh24igmIznHLB6H05Na4OgIEilRu0oQpYXo3LCNRrawHAcfDKq9004zJs+sAMt2X5AbY87CUCJ7IpqpSgdw==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/signature-v4@2.1.1:
+ resolution: {integrity: sha512-Hb7xub0NHuvvQD3YwDSdanBmYukoEkhqBjqoxo+bSdC0ryV9cTfgmNjuAQhTPYB6yeU7hTR+sPRiFMlxqv6kmg==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/eventstream-codec': 2.1.1
+ '@smithy/is-array-buffer': 2.1.1
+ '@smithy/types': 2.9.1
+ '@smithy/util-hex-encoding': 2.1.1
+ '@smithy/util-middleware': 2.1.1
+ '@smithy/util-uri-escape': 2.1.1
+ '@smithy/util-utf8': 2.1.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/smithy-client@2.3.1:
+ resolution: {integrity: sha512-YsTdU8xVD64r2pLEwmltrNvZV6XIAC50LN6ivDopdt+YiF/jGH6PY9zUOu0CXD/d8GMB8gbhnpPsdrjAXHS9QA==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/middleware-endpoint': 2.4.1
+ '@smithy/middleware-stack': 2.1.1
+ '@smithy/protocol-http': 3.1.1
+ '@smithy/types': 2.9.1
+ '@smithy/util-stream': 2.1.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/types@2.9.1:
+ resolution: {integrity: sha512-vjXlKNXyprDYDuJ7UW5iobdmyDm6g8dDG+BFUncAg/3XJaN45Gy5RWWWUVgrzIK7S4R1KWgIX5LeJcfvSI24bw==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/url-parser@2.1.1:
+ resolution: {integrity: sha512-qC9Bv8f/vvFIEkHsiNrUKYNl8uKQnn4BdhXl7VzQRP774AwIjiSMMwkbT+L7Fk8W8rzYVifzJNYxv1HwvfBo3Q==}
+ dependencies:
+ '@smithy/querystring-parser': 2.1.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/util-base64@2.1.1:
+ resolution: {integrity: sha512-UfHVpY7qfF/MrgndI5PexSKVTxSZIdz9InghTFa49QOvuu9I52zLPLUHXvHpNuMb1iD2vmc6R+zbv/bdMipR/g==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/util-buffer-from': 2.1.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/util-body-length-browser@2.1.1:
+ resolution: {integrity: sha512-ekOGBLvs1VS2d1zM2ER4JEeBWAvIOUKeaFch29UjjJsxmZ/f0L3K3x0dEETgh3Q9bkZNHgT+rkdl/J/VUqSRag==}
+ dependencies:
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/util-body-length-node@2.2.1:
+ resolution: {integrity: sha512-/ggJG+ta3IDtpNVq4ktmEUtOkH1LW64RHB5B0hcr5ZaWBmo96UX2cIOVbjCqqDickTXqBWZ4ZO0APuaPrD7Abg==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/util-buffer-from@2.1.1:
+ resolution: {integrity: sha512-clhNjbyfqIv9Md2Mg6FffGVrJxw7bgK7s3Iax36xnfVj6cg0fUG7I4RH0XgXJF8bxi+saY5HR21g2UPKSxVCXg==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/is-array-buffer': 2.1.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/util-config-provider@2.2.1:
+ resolution: {integrity: sha512-50VL/tx9oYYcjJn/qKqNy7sCtpD0+s8XEBamIFo4mFFTclKMNp+rsnymD796uybjiIquB7VCB/DeafduL0y2kw==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/util-defaults-mode-browser@2.1.1:
+ resolution: {integrity: sha512-lqLz/9aWRO6mosnXkArtRuQqqZBhNpgI65YDpww4rVQBuUT7qzKbDLG5AmnQTCiU4rOquaZO/Kt0J7q9Uic7MA==}
+ engines: {node: '>= 10.0.0'}
+ dependencies:
+ '@smithy/property-provider': 2.1.1
+ '@smithy/smithy-client': 2.3.1
+ '@smithy/types': 2.9.1
+ bowser: 2.11.0
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/util-defaults-mode-node@2.2.0:
+ resolution: {integrity: sha512-iFJp/N4EtkanFpBUtSrrIbtOIBf69KNuve03ic1afhJ9/korDxdM0c6cCH4Ehj/smI9pDCfVv+bqT3xZjF2WaA==}
+ engines: {node: '>= 10.0.0'}
+ dependencies:
+ '@smithy/config-resolver': 2.1.1
+ '@smithy/credential-provider-imds': 2.2.1
+ '@smithy/node-config-provider': 2.2.1
+ '@smithy/property-provider': 2.1.1
+ '@smithy/smithy-client': 2.3.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/util-endpoints@1.1.1:
+ resolution: {integrity: sha512-sI4d9rjoaekSGEtq3xSb2nMjHMx8QXcz2cexnVyRWsy4yQ9z3kbDpX+7fN0jnbdOp0b3KSTZJZ2Yb92JWSanLw==}
+ engines: {node: '>= 14.0.0'}
+ dependencies:
+ '@smithy/node-config-provider': 2.2.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/util-hex-encoding@2.1.1:
+ resolution: {integrity: sha512-3UNdP2pkYUUBGEXzQI9ODTDK+Tcu1BlCyDBaRHwyxhA+8xLP8agEKQq4MGmpjqb4VQAjq9TwlCQX0kP6XDKYLg==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/util-middleware@2.1.1:
+ resolution: {integrity: sha512-mKNrk8oz5zqkNcbcgAAepeJbmfUW6ogrT2Z2gDbIUzVzNAHKJQTYmH9jcy0jbWb+m7ubrvXKb6uMjkSgAqqsFA==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/util-retry@2.1.1:
+ resolution: {integrity: sha512-Mg+xxWPTeSPrthpC5WAamJ6PW4Kbo01Fm7lWM1jmGRvmrRdsd3192Gz2fBXAMURyXpaNxyZf6Hr/nQ4q70oVEA==}
+ engines: {node: '>= 14.0.0'}
+ dependencies:
+ '@smithy/service-error-classification': 2.1.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/util-stream@2.1.1:
+ resolution: {integrity: sha512-J7SMIpUYvU4DQN55KmBtvaMc7NM3CZ2iWICdcgaovtLzseVhAqFRYqloT3mh0esrFw+3VEK6nQFteFsTqZSECQ==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/fetch-http-handler': 2.4.1
+ '@smithy/node-http-handler': 2.3.1
+ '@smithy/types': 2.9.1
+ '@smithy/util-base64': 2.1.1
+ '@smithy/util-buffer-from': 2.1.1
+ '@smithy/util-hex-encoding': 2.1.1
+ '@smithy/util-utf8': 2.1.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/util-uri-escape@2.1.1:
+ resolution: {integrity: sha512-saVzI1h6iRBUVSqtnlOnc9ssU09ypo7n+shdQ8hBTZno/9rZ3AuRYvoHInV57VF7Qn7B+pFJG7qTzFiHxWlWBw==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/util-utf8@2.1.1:
+ resolution: {integrity: sha512-BqTpzYEcUMDwAKr7/mVRUtHDhs6ZoXDi9NypMvMfOr/+u1NW7JgqodPDECiiLboEm6bobcPcECxzjtQh865e9A==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/util-buffer-from': 2.1.1
+ tslib: 2.6.2
+ dev: false
+
+ /@smithy/util-waiter@2.1.1:
+ resolution: {integrity: sha512-kYy6BLJJNif+uqNENtJqWdXcpqo1LS+nj1AfXcDhOpqpSHJSAkVySLyZV9fkmuVO21lzGoxjvd1imGGJHph/IA==}
+ engines: {node: '>=14.0.0'}
+ dependencies:
+ '@smithy/abort-controller': 2.1.1
+ '@smithy/types': 2.9.1
+ tslib: 2.6.2
+ dev: false
+
+ /@surma/rollup-plugin-off-main-thread@2.2.3:
+ resolution: {integrity: sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ==}
+ dependencies:
+ ejs: 3.1.9
+ json5: 2.2.3
+ magic-string: 0.25.9
+ string.prototype.matchall: 4.0.10
+
+ /@swc/helpers@0.5.2:
+ resolution: {integrity: sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==}
+ dependencies:
+ tslib: 2.6.2
+
+ /@t3-oss/env-core@0.11.1(typescript@5.3.3)(zod@3.22.4):
+ resolution: {integrity: sha512-MaxOwEoG1ntCFoKJsS7nqwgcxLW1SJw238AJwfJeaz3P/8GtkxXZsPPolsz1AdYvUTbe3XvqZ/VCdfjt+3zmKw==}
+ peerDependencies:
+ typescript: '>=5.0.0'
+ zod: ^3.0.0
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ dependencies:
+ typescript: 5.3.3
+ zod: 3.22.4
+ dev: false
+
+ /@t3-oss/env-nextjs@0.11.1(typescript@5.3.3)(zod@3.22.4):
+ resolution: {integrity: sha512-rx2XL9+v6wtOqLNJbD5eD8OezKlQD1BtC0WvvtHwBgK66jnF5+wGqtgkKK4Ygie1LVmoDClths2T4tdFmRvGrQ==}
+ peerDependencies:
+ typescript: '>=5.0.0'
+ zod: ^3.0.0
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ dependencies:
+ '@t3-oss/env-core': 0.11.1(typescript@5.3.3)(zod@3.22.4)
+ typescript: 5.3.3
+ zod: 3.22.4
+ dev: false
+
+ /@tanstack/query-core@4.36.1:
+ resolution: {integrity: sha512-DJSilV5+ytBP1FbFcEJovv4rnnm/CokuVvrBEtW/Va9DvuJ3HksbXUJEpI0aV1KtuL4ZoO9AVE6PyNLzF7tLeA==}
+ dev: false
+
+ /@tanstack/react-query@4.36.1(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-y7ySVHFyyQblPl3J3eQBWpXZkliroki3ARnBKsdJchlgt7yJLRDUcf4B8soufgiYt3pEQIkBWBx1N9/ZPIeUWw==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+ react-native: '*'
+ peerDependenciesMeta:
+ react-dom:
+ optional: true
+ react-native:
+ optional: true
+ dependencies:
+ '@tanstack/query-core': 4.36.1
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ use-sync-external-store: 1.2.0(react@18.2.0)
+ dev: false
+
+ /@tanstack/react-virtual@3.1.3(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-YCzcbF/Ws/uZ0q3Z6fagH+JVhx4JLvbSflgldMgLsuvB8aXjZLLb3HvrEVxY480F9wFlBiXlvQxOyXb5ENPrNA==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
+ dependencies:
+ '@tanstack/virtual-core': 3.1.3
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@tanstack/virtual-core@3.1.3:
+ resolution: {integrity: sha512-Y5B4EYyv1j9V8LzeAoOVeTg0LI7Fo5InYKgAjkY1Pu9GjtUwX/EKxNcU7ng3sKr99WEf+bPTcktAeybyMOYo+g==}
+ dev: false
+
+ /@theguild/remark-mermaid@0.0.5(react@18.2.0):
+ resolution: {integrity: sha512-e+ZIyJkEv9jabI4m7q29wZtZv+2iwPGsXJ2d46Zi7e+QcFudiyuqhLhHG/3gX3ZEB+hxTch+fpItyMS8jwbIcw==}
+ peerDependencies:
+ react: ^18.2.0
+ dependencies:
+ mermaid: 10.9.0
+ react: 18.2.0
+ unist-util-visit: 5.0.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
+ /@theguild/remark-npm2yarn@0.2.1:
+ resolution: {integrity: sha512-jUTFWwDxtLEFtGZh/TW/w30ySaDJ8atKWH8dq2/IiQF61dPrGfETpl0WxD0VdBfuLOeU14/kop466oBSRO/5CA==}
+ dependencies:
+ npm-to-yarn: 2.2.1
+ unist-util-visit: 5.0.0
+ dev: false
+
+ /@trpc/client@10.45.0(@trpc/server@10.45.0):
+ resolution: {integrity: sha512-m091R1qte9rvkvL8N1e/mzrbb8S4gb+Q4ZNJnEGDgd7kic/6a8DFgSciBTiCoSp0YwOTVhyQzSrrA/sZI6PhBg==}
+ peerDependencies:
+ '@trpc/server': 10.45.0
+ dependencies:
+ '@trpc/server': 10.45.0
+ dev: false
+
+ /@trpc/next@10.45.0(@tanstack/react-query@4.36.1)(@trpc/client@10.45.0)(@trpc/react-query@10.45.0)(@trpc/server@10.45.0)(next@14.1.0)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-saXajAb5GBpos9BNEtq/BeTOxmM4oCP3kyuGlMopNtHoacr71xHCItFnLsPWffM4DVW88uOXCFWaOtpOs5ThBw==}
+ peerDependencies:
+ '@tanstack/react-query': ^4.18.0
+ '@trpc/client': 10.45.0
+ '@trpc/react-query': 10.45.0
+ '@trpc/server': 10.45.0
+ next: '*'
+ react: '>=16.8.0'
+ react-dom: '>=16.8.0'
+ dependencies:
+ '@tanstack/react-query': 4.36.1(react-dom@18.2.0)(react@18.2.0)
+ '@trpc/client': 10.45.0(@trpc/server@10.45.0)
+ '@trpc/react-query': 10.45.0(@tanstack/react-query@4.36.1)(@trpc/client@10.45.0)(@trpc/server@10.45.0)(react-dom@18.2.0)(react@18.2.0)
+ '@trpc/server': 10.45.0
+ next: 14.1.0(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0)
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@trpc/react-query@10.45.0(@tanstack/react-query@4.36.1)(@trpc/client@10.45.0)(@trpc/server@10.45.0)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-MMc2pLwoaLZVwvLQyzJv3uEmdG3lORhifhVzR/drtavwDYwt+OEvH0w3s1zC7RaDdFpc6Nj2kkpHmdoU7BlAAw==}
+ peerDependencies:
+ '@tanstack/react-query': ^4.18.0
+ '@trpc/client': 10.45.0
+ '@trpc/server': 10.45.0
+ react: '>=16.8.0'
+ react-dom: '>=16.8.0'
+ dependencies:
+ '@tanstack/react-query': 4.36.1(react-dom@18.2.0)(react@18.2.0)
+ '@trpc/client': 10.45.0(@trpc/server@10.45.0)
+ '@trpc/server': 10.45.0
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /@trpc/server@10.45.0:
+ resolution: {integrity: sha512-2Fwzv6nqpE0Ie/G7PeS0EVR89zLm+c1Mw7T+RAGtU807j4oaUx0zGkBXTu5u9AI+j+BYNN2GZxJcuDTAecbr1A==}
+ dev: false
+
+ /@types/acorn@4.0.6:
+ resolution: {integrity: sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ==}
+ dependencies:
+ '@types/estree': 1.0.5
+ dev: false
+
+ /@types/d3-scale-chromatic@3.0.3:
+ resolution: {integrity: sha512-laXM4+1o5ImZv3RpFAsTRn3TEkzqkytiOY0Dz0sq5cnd1dtNlk6sHLon4OvqaiJb28T0S/TdsBI3Sjsy+keJrw==}
+ dev: false
+
+ /@types/d3-scale@4.0.8:
+ resolution: {integrity: sha512-gkK1VVTr5iNiYJ7vWDI+yUFFlszhNMtVeneJ6lUTKPjprsvLLI9/tgEGiXJOnlINJA8FyA88gfnQsHbybVZrYQ==}
+ dependencies:
+ '@types/d3-time': 3.0.3
+ dev: false
+
+ /@types/d3-time@3.0.3:
+ resolution: {integrity: sha512-2p6olUZ4w3s+07q3Tm2dbiMZy5pCDfYwtLXXHUnVzXgQlZ/OyPtUz6OL382BkOuGlLXqfT+wqv8Fw2v8/0geBw==}
+ dev: false
+
+ /@types/debug@4.1.12:
+ resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
+ dependencies:
+ '@types/ms': 0.7.34
+ dev: false
+
+ /@types/eslint-scope@3.7.7:
+ resolution: {integrity: sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg==}
+ dependencies:
+ '@types/eslint': 8.56.2
+ '@types/estree': 1.0.5
+ dev: false
+
+ /@types/eslint@8.56.2:
+ resolution: {integrity: sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw==}
+ dependencies:
+ '@types/estree': 1.0.5
+ '@types/json-schema': 7.0.15
+
+ /@types/estree-jsx@1.0.5:
+ resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==}
+ dependencies:
+ '@types/estree': 1.0.5
+ dev: false
+
+ /@types/estree@0.0.39:
+ resolution: {integrity: sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==}
+
+ /@types/estree@1.0.5:
+ resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
+
+ /@types/glob@7.2.0:
+ resolution: {integrity: sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==}
+ dependencies:
+ '@types/minimatch': 5.1.2
+ '@types/node': 18.19.8
+ dev: false
+
+ /@types/hast@2.3.10:
+ resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==}
+ dependencies:
+ '@types/unist': 2.0.10
+ dev: false
+
+ /@types/hast@3.0.4:
+ resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==}
+ dependencies:
+ '@types/unist': 3.0.2
+ dev: false
+
+ /@types/js-yaml@4.0.9:
+ resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==}
+ dev: false
+
+ /@types/json-schema@7.0.15:
+ resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+
+ /@types/json5@0.0.29:
+ resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
+ dev: true
+
+ /@types/katex@0.16.7:
+ resolution: {integrity: sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ==}
+ dev: false
+
+ /@types/mdast@3.0.15:
+ resolution: {integrity: sha512-LnwD+mUEfxWMa1QpDraczIn6k0Ee3SMicuYSSzS6ZYl2gKS09EClnJYGd8Du6rfc5r/GZEk5o1mRb8TaTj03sQ==}
+ dependencies:
+ '@types/unist': 2.0.10
+ dev: false
+
+ /@types/mdast@4.0.3:
+ resolution: {integrity: sha512-LsjtqsyF+d2/yFOYaN22dHZI1Cpwkrj+g06G8+qtUKlhovPW89YhqSnfKtMbkgmEtYpH2gydRNULd6y8mciAFg==}
+ dependencies:
+ '@types/unist': 3.0.2
+ dev: false
+
+ /@types/mdx@2.0.11:
+ resolution: {integrity: sha512-HM5bwOaIQJIQbAYfax35HCKxx7a3KrK3nBtIqJgSOitivTD1y3oW9P3rxY9RkXYPUk7y/AjAohfHKmFpGE79zw==}
+ dev: false
+
+ /@types/minimatch@5.1.2:
+ resolution: {integrity: sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==}
+ dev: false
+
+ /@types/ms@0.7.34:
+ resolution: {integrity: sha512-nG96G3Wp6acyAgJqGasjODb+acrI7KltPiRxzHPXnP3NgI28bpQDRv53olbqGXbfcgF5aiiHmO3xpwEpS5Ld9g==}
+ dev: false
+
+ /@types/next-pwa@5.6.9(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-KcymH+MtFYB5KVKIOH1DMqd0wUb8VLCxzHtsaRQQ7S8sGOaTH24Lo2vGZf6/0Ok9e+xWCKhqsSt6cgDJTk91Iw==}
+ dependencies:
+ '@types/node': 18.19.8
+ '@types/react': 18.2.48
+ '@types/react-dom': 18.2.18
+ next: 13.5.6(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0)
+ workbox-build: 6.6.0
+ transitivePeerDependencies:
+ - '@babel/core'
+ - '@opentelemetry/api'
+ - '@types/babel__core'
+ - babel-plugin-macros
+ - react
+ - react-dom
+ - sass
+ - supports-color
+ dev: true
+
+ /@types/node@18.19.8:
+ resolution: {integrity: sha512-g1pZtPhsvGVTwmeVoexWZLTQaOvXwoSq//pTL0DHeNzUDrFnir4fgETdhjhIxjVnN+hKOuh98+E1eMLnUXstFg==}
+ dependencies:
+ undici-types: 5.26.5
+
+ /@types/nodemailer@6.4.15:
+ resolution: {integrity: sha512-0EBJxawVNjPkng1zm2vopRctuWVCxk34JcIlRuXSf54habUWdz1FB7wHDqOqvDa8Mtpt0Q3LTXQkAs2LNyK5jQ==}
+ dependencies:
+ '@types/node': 18.19.8
+ dev: true
+
+ /@types/prop-types@15.7.11:
+ resolution: {integrity: sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==}
+
+ /@types/react-dom@18.2.18:
+ resolution: {integrity: sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==}
+ dependencies:
+ '@types/react': 18.2.48
+
+ /@types/react@18.2.48:
+ resolution: {integrity: sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w==}
+ dependencies:
+ '@types/prop-types': 15.7.11
+ '@types/scheduler': 0.16.8
+ csstype: 3.1.3
+
+ /@types/resolve@1.17.1:
+ resolution: {integrity: sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==}
+ dependencies:
+ '@types/node': 18.19.8
+
+ /@types/scheduler@0.16.8:
+ resolution: {integrity: sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A==}
+
+ /@types/semver@7.5.6:
+ resolution: {integrity: sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==}
+ dev: true
+
+ /@types/trusted-types@2.0.7:
+ resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
+
+ /@types/unist@2.0.10:
+ resolution: {integrity: sha512-IfYcSBWE3hLpBg8+X2SEa8LVkJdJEkT2Ese2aaLs3ptGdVtABxndrMaxuFlQ1qdFf9Q5rDvDpxI3WwgvKFAsQA==}
+ dev: false
+
+ /@types/unist@3.0.2:
+ resolution: {integrity: sha512-dqId9J8K/vGi5Zr7oo212BGii5m3q5Hxlkwy3WpYuKPklmBEvsbMYYyLxAQpSffdLl/gdW0XUpKWFvYmyoWCoQ==}
+ dev: false
+
+ /@types/web-push@3.6.3:
+ resolution: {integrity: sha512-v3oT4mMJsHeJ/rraliZ+7TbZtr5bQQuxcgD7C3/1q/zkAj29c8RE0F9lVZVu3hiQe5Z9fYcBreV7TLnfKR+4mg==}
+ dependencies:
+ '@types/node': 18.19.8
+ dev: true
+
+ /@typescript-eslint/eslint-plugin@6.19.0(@typescript-eslint/parser@6.19.0)(eslint@8.56.0)(typescript@5.3.3):
+ resolution: {integrity: sha512-DUCUkQNklCQYnrBSSikjVChdc84/vMPDQSgJTHBZ64G9bA9w0Crc0rd2diujKbTdp6w2J47qkeHQLoi0rpLCdg==}
+ 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
+ dependencies:
+ '@eslint-community/regexpp': 4.10.0
+ '@typescript-eslint/parser': 6.19.0(eslint@8.56.0)(typescript@5.3.3)
+ '@typescript-eslint/scope-manager': 6.19.0
+ '@typescript-eslint/type-utils': 6.19.0(eslint@8.56.0)(typescript@5.3.3)
+ '@typescript-eslint/utils': 6.19.0(eslint@8.56.0)(typescript@5.3.3)
+ '@typescript-eslint/visitor-keys': 6.19.0
+ debug: 4.3.4
+ eslint: 8.56.0
+ graphemer: 1.4.0
+ ignore: 5.3.0
+ natural-compare: 1.4.0
+ semver: 7.5.4
+ ts-api-utils: 1.0.3(typescript@5.3.3)
+ typescript: 5.3.3
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@typescript-eslint/parser@6.19.0(eslint@8.56.0)(typescript@5.3.3):
+ resolution: {integrity: sha512-1DyBLG5SH7PYCd00QlroiW60YJ4rWMuUGa/JBV0iZuqi4l4IK3twKPq5ZkEebmGqRjXWVgsUzfd3+nZveewgow==}
+ engines: {node: ^16.0.0 || >=18.0.0}
+ peerDependencies:
+ eslint: ^7.0.0 || ^8.0.0
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ dependencies:
+ '@typescript-eslint/scope-manager': 6.19.0
+ '@typescript-eslint/types': 6.19.0
+ '@typescript-eslint/typescript-estree': 6.19.0(typescript@5.3.3)
+ '@typescript-eslint/visitor-keys': 6.19.0
+ debug: 4.3.4
+ eslint: 8.56.0
+ typescript: 5.3.3
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@typescript-eslint/scope-manager@6.19.0:
+ resolution: {integrity: sha512-dO1XMhV2ehBI6QN8Ufi7I10wmUovmLU0Oru3n5LVlM2JuzB4M+dVphCPLkVpKvGij2j/pHBWuJ9piuXx+BhzxQ==}
+ engines: {node: ^16.0.0 || >=18.0.0}
+ dependencies:
+ '@typescript-eslint/types': 6.19.0
+ '@typescript-eslint/visitor-keys': 6.19.0
+ dev: true
+
+ /@typescript-eslint/type-utils@6.19.0(eslint@8.56.0)(typescript@5.3.3):
+ resolution: {integrity: sha512-mcvS6WSWbjiSxKCwBcXtOM5pRkPQ6kcDds/juxcy/727IQr3xMEcwr/YLHW2A2+Fp5ql6khjbKBzOyjuPqGi/w==}
+ engines: {node: ^16.0.0 || >=18.0.0}
+ peerDependencies:
+ eslint: ^7.0.0 || ^8.0.0
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ dependencies:
+ '@typescript-eslint/typescript-estree': 6.19.0(typescript@5.3.3)
+ '@typescript-eslint/utils': 6.19.0(eslint@8.56.0)(typescript@5.3.3)
+ debug: 4.3.4
+ eslint: 8.56.0
+ ts-api-utils: 1.0.3(typescript@5.3.3)
+ typescript: 5.3.3
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@typescript-eslint/types@6.19.0:
+ resolution: {integrity: sha512-lFviGV/vYhOy3m8BJ/nAKoAyNhInTdXpftonhWle66XHAtT1ouBlkjL496b5H5hb8dWXHwtypTqgtb/DEa+j5A==}
+ engines: {node: ^16.0.0 || >=18.0.0}
+ dev: true
+
+ /@typescript-eslint/typescript-estree@6.19.0(typescript@5.3.3):
+ resolution: {integrity: sha512-o/zefXIbbLBZ8YJ51NlkSAt2BamrK6XOmuxSR3hynMIzzyMY33KuJ9vuMdFSXW+H0tVvdF9qBPTHA91HDb4BIQ==}
+ engines: {node: ^16.0.0 || >=18.0.0}
+ peerDependencies:
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ dependencies:
+ '@typescript-eslint/types': 6.19.0
+ '@typescript-eslint/visitor-keys': 6.19.0
+ debug: 4.3.4
+ globby: 11.1.0
+ is-glob: 4.0.3
+ minimatch: 9.0.3
+ semver: 7.5.4
+ ts-api-utils: 1.0.3(typescript@5.3.3)
+ typescript: 5.3.3
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@typescript-eslint/utils@6.19.0(eslint@8.56.0)(typescript@5.3.3):
+ resolution: {integrity: sha512-QR41YXySiuN++/dC9UArYOg4X86OAYP83OWTewpVx5ct1IZhjjgTLocj7QNxGhWoTqknsgpl7L+hGygCO+sdYw==}
+ engines: {node: ^16.0.0 || >=18.0.0}
+ peerDependencies:
+ eslint: ^7.0.0 || ^8.0.0
+ dependencies:
+ '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0)
+ '@types/json-schema': 7.0.15
+ '@types/semver': 7.5.6
+ '@typescript-eslint/scope-manager': 6.19.0
+ '@typescript-eslint/types': 6.19.0
+ '@typescript-eslint/typescript-estree': 6.19.0(typescript@5.3.3)
+ eslint: 8.56.0
+ semver: 7.5.4
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+ dev: true
+
+ /@typescript-eslint/visitor-keys@6.19.0:
+ resolution: {integrity: sha512-hZaUCORLgubBvtGpp1JEFEazcuEdfxta9j4iUwdSAr7mEsYYAp3EAUyCZk3VEEqGj6W+AV4uWyrDGtrlawAsgQ==}
+ engines: {node: ^16.0.0 || >=18.0.0}
+ dependencies:
+ '@typescript-eslint/types': 6.19.0
+ eslint-visitor-keys: 3.4.3
+ dev: true
+
+ /@ungap/structured-clone@1.2.0:
+ resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
+
+ /@webassemblyjs/ast@1.11.6:
+ resolution: {integrity: sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q==}
+ dependencies:
+ '@webassemblyjs/helper-numbers': 1.11.6
+ '@webassemblyjs/helper-wasm-bytecode': 1.11.6
+ dev: false
+
+ /@webassemblyjs/floating-point-hex-parser@1.11.6:
+ resolution: {integrity: sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw==}
+ dev: false
+
+ /@webassemblyjs/helper-api-error@1.11.6:
+ resolution: {integrity: sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q==}
+ dev: false
+
+ /@webassemblyjs/helper-buffer@1.11.6:
+ resolution: {integrity: sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA==}
+ dev: false
+
+ /@webassemblyjs/helper-numbers@1.11.6:
+ resolution: {integrity: sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g==}
+ dependencies:
+ '@webassemblyjs/floating-point-hex-parser': 1.11.6
+ '@webassemblyjs/helper-api-error': 1.11.6
+ '@xtuc/long': 4.2.2
+ dev: false
+
+ /@webassemblyjs/helper-wasm-bytecode@1.11.6:
+ resolution: {integrity: sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA==}
+ 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
+ dev: false
+
+ /@webassemblyjs/ieee754@1.11.6:
+ resolution: {integrity: sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg==}
+ dependencies:
+ '@xtuc/ieee754': 1.2.0
+ dev: false
+
+ /@webassemblyjs/leb128@1.11.6:
+ resolution: {integrity: sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ==}
+ dependencies:
+ '@xtuc/long': 4.2.2
+ dev: false
+
+ /@webassemblyjs/utf8@1.11.6:
+ resolution: {integrity: sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA==}
+ 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
+ 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
+ dev: false
+
+ /@webassemblyjs/wasm-opt@1.11.6:
+ resolution: {integrity: sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g==}
+ dependencies:
+ '@webassemblyjs/ast': 1.11.6
+ '@webassemblyjs/helper-buffer': 1.11.6
+ '@webassemblyjs/wasm-gen': 1.11.6
+ '@webassemblyjs/wasm-parser': 1.11.6
+ 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
+ dev: false
+
+ /@webassemblyjs/wast-printer@1.11.6:
+ resolution: {integrity: sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A==}
+ dependencies:
+ '@webassemblyjs/ast': 1.11.6
+ '@xtuc/long': 4.2.2
+ dev: false
+
+ /@xtuc/ieee754@1.2.0:
+ resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==}
+ dev: false
+
+ /@xtuc/long@4.2.2:
+ resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==}
+ dev: false
+
+ /abbrev@2.0.0:
+ resolution: {integrity: sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+ dev: false
+
+ /acorn-import-assertions@1.9.0(acorn@8.11.3):
+ resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==}
+ peerDependencies:
+ acorn: ^8
+ dependencies:
+ acorn: 8.11.3
+ dev: false
+
+ /acorn-jsx@5.3.2(acorn@8.11.3):
+ resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+ peerDependencies:
+ acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+ dependencies:
+ acorn: 8.11.3
+
+ /acorn@8.11.3:
+ resolution: {integrity: sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ /agent-base@7.1.0:
+ resolution: {integrity: sha512-o/zjMZRhJxny7OyEF+Op8X+efiELC7k7yOjMzgfzVqOzXqkBkWI79YoTdOtsuWd5BWhAGAuOY/Xa6xpiaWXiNg==}
+ engines: {node: '>= 14'}
+ dependencies:
+ debug: 4.3.4
+ transitivePeerDependencies:
+ - supports-color
+ 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
+ dev: false
+
+ /ajv-keywords@3.5.2(ajv@6.12.6):
+ resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==}
+ peerDependencies:
+ ajv: ^6.9.1
+ dependencies:
+ ajv: 6.12.6
+ dev: false
+
+ /ajv-keywords@5.1.0(ajv@8.12.0):
+ resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==}
+ peerDependencies:
+ ajv: ^8.8.2
+ dependencies:
+ ajv: 8.12.0
+ fast-deep-equal: 3.1.3
+ dev: false
+
+ /ajv@6.12.6:
+ resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+ 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==}
+ 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-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'}
+
+ /ansi-sequence-parser@1.1.1:
+ resolution: {integrity: sha512-vJXt3yiaUL4UU546s3rPXlsry/RnM730G1+HkpKE012AN0sx1eOrxSu95oKDIonskeLTijMgqWZ3uDEe3NFvyg==}
+ dev: false
+
+ /ansi-styles@3.2.1:
+ resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
+ engines: {node: '>=4'}
+ dependencies:
+ color-convert: 1.9.3
+
+ /ansi-styles@4.3.0:
+ resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+ engines: {node: '>=8'}
+ dependencies:
+ color-convert: 2.0.1
+
+ /ansi-styles@6.2.1:
+ resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
+ engines: {node: '>=12'}
+
+ /any-promise@1.3.0:
+ resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==}
+
+ /anymatch@3.1.3:
+ resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+ engines: {node: '>= 8'}
+ dependencies:
+ normalize-path: 3.0.0
+ picomatch: 2.3.1
+
+ /arch@2.2.0:
+ resolution: {integrity: sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ==}
+ dev: false
+
+ /arg@1.0.0:
+ resolution: {integrity: sha512-Wk7TEzl1KqvTGs/uyhmHO/3XLd3t1UeU4IstvPXVzGPM522cTjqjNZ99esCkcL52sjqjo8e8CTBcWhkxvGzoAw==}
+ dev: false
+
+ /arg@5.0.2:
+ resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==}
+
+ /argparse@1.0.10:
+ resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
+ dependencies:
+ sprintf-js: 1.0.3
+ dev: false
+
+ /argparse@2.0.1:
+ resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+ /aria-hidden@1.2.3:
+ resolution: {integrity: sha512-xcLxITLe2HYa1cnYnwCjkOO1PqUHQpozB8x9AR0OgWN2woOBi5kSDVxKfd0b7sb1hw5qFeJhXm9H1nu3xSfLeQ==}
+ engines: {node: '>=10'}
+ dependencies:
+ tslib: 2.6.2
+ dev: false
+
+ /aria-query@5.3.0:
+ resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==}
+ dependencies:
+ dequal: 2.0.3
+ dev: true
+
+ /array-buffer-byte-length@1.0.0:
+ resolution: {integrity: sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==}
+ dependencies:
+ call-bind: 1.0.5
+ is-array-buffer: 3.0.2
+
+ /array-includes@3.1.7:
+ resolution: {integrity: sha512-dlcsNBIiWhPkHdOEEKnehA+RNUWDc4UqFtnIXU4uuYDPtA4LDkr7qip2p0VvFAEXNDr0yWZ9PJyIRiGjRLQzwQ==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.5
+ define-properties: 1.2.1
+ es-abstract: 1.22.3
+ get-intrinsic: 1.2.2
+ is-string: 1.0.7
+ dev: true
+
+ /array-union@1.0.2:
+ resolution: {integrity: sha512-Dxr6QJj/RdU/hCaBjOfxW+q6lyuVE6JFWIrAUpuOOhoJJoQ99cUn3igRaHVB5P9WrgFVN0FfArM3x0cueOU8ng==}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ array-uniq: 1.0.3
+ dev: false
+
+ /array-union@2.1.0:
+ resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
+ engines: {node: '>=8'}
+
+ /array-uniq@1.0.3:
+ resolution: {integrity: sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==}
+ engines: {node: '>=0.10.0'}
+ dev: false
+
+ /array.prototype.findlastindex@1.2.3:
+ resolution: {integrity: sha512-LzLoiOMAxvy+Gd3BAq3B7VeIgPdo+Q8hthvKtXybMvRV0jrXfJM/t8mw7nNlpEcVlVUnCnM2KSX4XU5HmpodOA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.5
+ define-properties: 1.2.1
+ es-abstract: 1.22.3
+ es-shim-unscopables: 1.0.2
+ get-intrinsic: 1.2.2
+ dev: true
+
+ /array.prototype.flat@1.3.2:
+ resolution: {integrity: sha512-djYB+Zx2vLewY8RWlNCUdHjDXs2XOgm602S9E7P/UpHgfeHL00cRiIF+IN/G/aUJ7kGPb6yO/ErDI5V2s8iycA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.5
+ define-properties: 1.2.1
+ es-abstract: 1.22.3
+ es-shim-unscopables: 1.0.2
+ dev: true
+
+ /array.prototype.flatmap@1.3.2:
+ resolution: {integrity: sha512-Ewyx0c9PmpcsByhSW4r+9zDU7sGjFc86qf/kKtuSCRdhfbk0SNLLkaT5qvcHnRGgc5NP/ly/y+qkXkqONX54CQ==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.5
+ define-properties: 1.2.1
+ es-abstract: 1.22.3
+ es-shim-unscopables: 1.0.2
+ dev: true
+
+ /array.prototype.tosorted@1.1.2:
+ resolution: {integrity: sha512-HuQCHOlk1Weat5jzStICBCd83NxiIMwqDg/dHEsoefabn/hJRj5pVdWcPUSpRrwhwxZOsQassMpgN/xRYFBMIg==}
+ dependencies:
+ call-bind: 1.0.5
+ define-properties: 1.2.1
+ es-abstract: 1.22.3
+ es-shim-unscopables: 1.0.2
+ get-intrinsic: 1.2.2
+ dev: true
+
+ /arraybuffer.prototype.slice@1.0.2:
+ resolution: {integrity: sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ array-buffer-byte-length: 1.0.0
+ call-bind: 1.0.5
+ define-properties: 1.2.1
+ es-abstract: 1.22.3
+ get-intrinsic: 1.2.2
+ is-array-buffer: 3.0.2
+ is-shared-array-buffer: 1.0.2
+
+ /asn1.js@5.4.1:
+ resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==}
+ dependencies:
+ bn.js: 4.12.0
+ inherits: 2.0.4
+ minimalistic-assert: 1.0.1
+ safer-buffer: 2.1.2
+ dev: false
+
+ /ast-types-flow@0.0.8:
+ resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==}
+ dev: true
+
+ /astring@1.8.6:
+ resolution: {integrity: sha512-ISvCdHdlTDlH5IpxQJIex7BWBywFWgjJSVdwst+/iQCoEYnyOaQ95+X1JGshuBjGp6nxKUy1jMgE3zPqN7fQdg==}
+ hasBin: true
+ dev: false
+
+ /async@3.2.5:
+ resolution: {integrity: sha512-baNZyqaaLhyLVKm/DlvdW051MSgO6b8eVfIezl9E5PqWxFgzLm/wQntEW4zOytVburDEr0JlALEpdOFwvErLsg==}
+
+ /asynciterator.prototype@1.0.0:
+ resolution: {integrity: sha512-wwHYEIS0Q80f5mosx3L/dfG5t5rjEa9Ft51GTaNt862EnpyGHpgz2RkZvLPp1oF5TnAiTohkEKVEu8pQPJI7Vg==}
+ dependencies:
+ has-symbols: 1.0.3
+ dev: true
+
+ /at-least-node@1.0.0:
+ resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
+ engines: {node: '>= 4.0.0'}
+
+ /autoprefixer@10.4.17(postcss@8.4.33):
+ resolution: {integrity: sha512-/cpVNRLSfhOtcGflT13P2794gVSgmPgTR+erw5ifnMLZb0UnSlkK4tquLmkd3BhA+nLo5tX8Cu0upUsGKvKbmg==}
+ engines: {node: ^10 || ^12 || >=14}
+ hasBin: true
+ peerDependencies:
+ postcss: ^8.1.0
+ dependencies:
+ browserslist: 4.22.2
+ caniuse-lite: 1.0.30001579
+ fraction.js: 4.3.7
+ normalize-range: 0.1.2
+ picocolors: 1.0.0
+ postcss: 8.4.33
+ postcss-value-parser: 4.2.0
+ dev: true
+
+ /available-typed-arrays@1.0.5:
+ resolution: {integrity: sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==}
+ engines: {node: '>= 0.4'}
+
+ /axe-core@4.7.0:
+ resolution: {integrity: sha512-M0JtH+hlOL5pLQwHOLNYZaXuhqmvS8oExsqB1SBYgA4Dk7u/xx+YdGHXaK5pyUfed5mYXdlYiphWq3G8cRi5JQ==}
+ engines: {node: '>=4'}
+ dev: true
+
+ /axobject-query@3.2.1:
+ resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==}
+ dependencies:
+ dequal: 2.0.3
+ dev: true
+
+ /b4a@1.6.6:
+ resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==}
+ dev: false
+
+ /babel-loader@8.3.0(@babel/core@7.23.9)(webpack@5.90.1):
+ resolution: {integrity: sha512-H8SvsMF+m9t15HNLMipppzkC+Y2Yq+v3SonZyU70RBL/h1gxPkH08Ot8pEE9Z4Kd+czyWJClmFS8qzIP9OZ04Q==}
+ engines: {node: '>= 8.9'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+ webpack: '>=2'
+ dependencies:
+ '@babel/core': 7.23.9
+ find-cache-dir: 3.3.2
+ loader-utils: 2.0.4
+ make-dir: 3.1.0
+ schema-utils: 2.7.1
+ webpack: 5.90.1
+ dev: false
+
+ /babel-loader@9.1.3(@babel/core@7.23.9)(webpack@5.90.1):
+ resolution: {integrity: sha512-xG3ST4DglodGf8qSwv0MdeWLhrDsw/32QMdTO5T1ZIp9gQur0HkCyFs7Awskr10JKXFXwpAhiCuYX5oGXnRGbw==}
+ engines: {node: '>= 14.15.0'}
+ peerDependencies:
+ '@babel/core': ^7.12.0
+ webpack: '>=5'
+ dependencies:
+ '@babel/core': 7.23.9
+ find-cache-dir: 4.0.0
+ schema-utils: 4.2.0
+ webpack: 5.90.1
+ dev: false
+
+ /babel-plugin-polyfill-corejs2@0.4.8(@babel/core@7.23.7):
+ resolution: {integrity: sha512-OtIuQfafSzpo/LhnJaykc0R/MMnuLSSVjVYy9mHArIZ9qTCSZ6TpWCuEKZYVoN//t8HqBNScHrOtCrIK5IaGLg==}
+ peerDependencies:
+ '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+ dependencies:
+ '@babel/compat-data': 7.23.5
+ '@babel/core': 7.23.7
+ '@babel/helper-define-polyfill-provider': 0.5.0(@babel/core@7.23.7)
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ /babel-plugin-polyfill-corejs3@0.8.7(@babel/core@7.23.7):
+ resolution: {integrity: sha512-KyDvZYxAzkC0Aj2dAPyDzi2Ym15e5JKZSK+maI7NAwSqofvuFglbSsxE7wUOvTg9oFVnHMzVzBKcqEb4PJgtOA==}
+ peerDependencies:
+ '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-define-polyfill-provider': 0.4.4(@babel/core@7.23.7)
+ core-js-compat: 3.35.1
+ transitivePeerDependencies:
+ - supports-color
+
+ /babel-plugin-polyfill-regenerator@0.5.5(@babel/core@7.23.7):
+ resolution: {integrity: sha512-OJGYZlhLqBh2DDHeqAxWB1XIvr49CxiJ2gIt61/PU55CQK4Z58OzMqjDe1zwQdQk+rBYsRc+1rJmdajM3gimHg==}
+ peerDependencies:
+ '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+ dependencies:
+ '@babel/core': 7.23.7
+ '@babel/helper-define-polyfill-provider': 0.5.0(@babel/core@7.23.7)
+ transitivePeerDependencies:
+ - supports-color
+
+ /bail@2.0.2:
+ resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==}
+ dev: false
+
+ /balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+ /bare-events@2.4.2:
+ resolution: {integrity: sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==}
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /bare-fs@2.3.1:
+ resolution: {integrity: sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==}
+ requiresBuild: true
+ dependencies:
+ bare-events: 2.4.2
+ bare-path: 2.1.3
+ bare-stream: 2.1.3
+ dev: false
+ optional: true
+
+ /bare-os@2.4.0:
+ resolution: {integrity: sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg==}
+ requiresBuild: true
+ dev: false
+ optional: true
+
+ /bare-path@2.1.3:
+ resolution: {integrity: sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==}
+ requiresBuild: true
+ dependencies:
+ bare-os: 2.4.0
+ dev: false
+ optional: true
+
+ /bare-stream@2.1.3:
+ resolution: {integrity: sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ==}
+ requiresBuild: true
+ dependencies:
+ streamx: 2.18.0
+ dev: false
+ optional: true
+
+ /base64-js@1.5.1:
+ resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
+ dev: false
+
+ /big.js@5.2.2:
+ resolution: {integrity: sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==}
+ dev: false
+
+ /binary-extensions@2.2.0:
+ resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
+ engines: {node: '>=8'}
+
+ /bl@4.1.0:
+ resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==}
+ dependencies:
+ buffer: 5.7.1
+ inherits: 2.0.4
+ readable-stream: 3.6.2
+ dev: false
+
+ /bn.js@4.12.0:
+ resolution: {integrity: sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==}
+ dev: false
+
+ /boring-avatars@1.10.1:
+ resolution: {integrity: sha512-WcgHDeLrazCR03CDPEvCchLsUecZAZvs4F6FnMiGlTEjyQQf15Q5TRl4EUaAQ1dacvhPq7lC9EOTWkCojQ6few==}
+ dev: false
+
+ /bowser@2.11.0:
+ resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==}
+ dev: false
+
+ /brace-expansion@1.1.11:
+ resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+
+ /brace-expansion@2.0.1:
+ resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
+ dependencies:
+ balanced-match: 1.0.2
+
+ /braces@3.0.2:
+ resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
+ engines: {node: '>=8'}
+ dependencies:
+ fill-range: 7.0.1
+
+ /browserslist@4.22.2:
+ resolution: {integrity: sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+ dependencies:
+ caniuse-lite: 1.0.30001579
+ electron-to-chromium: 1.4.640
+ node-releases: 2.0.14
+ update-browserslist-db: 1.0.13(browserslist@4.22.2)
+
+ /browserslist@4.22.3:
+ resolution: {integrity: sha512-UAp55yfwNv0klWNapjs/ktHoguxuQNGnOzxYmfnXIS+8AsRDZkSDxg7R1AX3GKzn078SBI5dzwzj/Yx0Or0e3A==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+ dependencies:
+ caniuse-lite: 1.0.30001582
+ electron-to-chromium: 1.4.655
+ node-releases: 2.0.14
+ update-browserslist-db: 1.0.13(browserslist@4.22.3)
+ dev: false
+
+ /buffer-equal-constant-time@1.0.1:
+ resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
+ dev: false
+
+ /buffer-from@1.1.2:
+ resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
+
+ /buffer@5.7.1:
+ resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
+ dependencies:
+ base64-js: 1.5.1
+ ieee754: 1.2.1
+ dev: false
+
+ /builtin-modules@3.3.0:
+ resolution: {integrity: sha512-zhaCDicdLuWN5UbN5IMnFqNMhNfo919sH85y2/ea+5Yg9TsTkeZxpL+JLbp6cgYFS4sRLp3YV4S6yDuqVWHYOw==}
+ engines: {node: '>=6'}
+
+ /busboy@1.6.0:
+ resolution: {integrity: sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==}
+ engines: {node: '>=10.16.0'}
+ dependencies:
+ streamsearch: 1.1.0
+
+ /call-bind@1.0.5:
+ resolution: {integrity: sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==}
+ dependencies:
+ function-bind: 1.1.2
+ get-intrinsic: 1.2.2
+ set-function-length: 1.2.0
+
+ /callsites@3.1.0:
+ resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+ engines: {node: '>=6'}
+ dev: true
+
+ /camelcase-css@2.0.1:
+ resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==}
+ engines: {node: '>= 6'}
+
+ /caniuse-lite@1.0.30001579:
+ resolution: {integrity: sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA==}
+
+ /caniuse-lite@1.0.30001582:
+ resolution: {integrity: sha512-vsJG3V5vgfduaQGVxL53uSX/HUzxyr2eA8xCo36OLal7sRcSZbibJtLeh0qja4sFOr/QQGt4opB4tOy+eOgAxg==}
+ dev: false
+
+ /ccount@2.0.1:
+ resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
+ dev: false
+
+ /chalk@2.3.0:
+ resolution: {integrity: sha512-Az5zJR2CBujap2rqXGaJKaPHyJ0IrUimvYNX+ncCy8PJP4ltOGTrHUIo097ZaL2zMeKYpiCdqDvS6zdrTFok3Q==}
+ engines: {node: '>=4'}
+ dependencies:
+ ansi-styles: 3.2.1
+ escape-string-regexp: 1.0.5
+ supports-color: 4.5.0
+ dev: false
+
+ /chalk@2.4.2:
+ resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
+ engines: {node: '>=4'}
+ 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'}
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+
+ /character-entities-html4@2.1.0:
+ resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==}
+ dev: false
+
+ /character-entities-legacy@3.0.0:
+ resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==}
+ dev: false
+
+ /character-entities@2.0.2:
+ resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==}
+ dev: false
+
+ /character-reference-invalid@2.0.1:
+ resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==}
+ dev: false
+
+ /chokidar@3.5.3:
+ resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
+ engines: {node: '>= 8.10.0'}
+ dependencies:
+ anymatch: 3.1.3
+ braces: 3.0.2
+ 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
+
+ /chownr@1.1.4:
+ resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==}
+ dev: false
+
+ /chrome-trace-event@1.0.3:
+ resolution: {integrity: sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==}
+ engines: {node: '>=6.0'}
+ dev: false
+
+ /class-variance-authority@0.7.0:
+ resolution: {integrity: sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==}
+ dependencies:
+ clsx: 2.0.0
+ dev: false
+
+ /clean-webpack-plugin@4.0.0(webpack@5.90.1):
+ resolution: {integrity: sha512-WuWE1nyTNAyW5T7oNyys2EN0cfP2fdRxhxnIQWiAp0bMabPdHhoGxM8A6YL2GhqwgrPnnaemVE7nv5XJ2Fhh2w==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ webpack: '>=4.0.0 <6.0.0'
+ dependencies:
+ del: 4.1.1
+ webpack: 5.90.1
+ dev: false
+
+ /client-only@0.0.1:
+ resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
+
+ /clipboardy@1.2.2:
+ resolution: {integrity: sha512-16KrBOV7bHmHdxcQiCvfUFYVFyEah4FI8vYT1Fr7CGSA4G+xBWMEfUEQJS1hxeHGtI9ju1Bzs9uXSbj5HZKArw==}
+ engines: {node: '>=4'}
+ dependencies:
+ arch: 2.2.0
+ execa: 0.8.0
+ dev: false
+
+ /clsx@2.0.0:
+ resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==}
+ engines: {node: '>=6'}
+ dev: false
+
+ /clsx@2.1.0:
+ resolution: {integrity: sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==}
+ engines: {node: '>=6'}
+ dev: false
+
+ /cmdk@0.2.0(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-JQpKvEOb86SnvMZbYaFKYhvzFntWBeSZdyii0rZPhKJj9uwJBxu4DaVYDrRN7r3mPop56oPhRw+JYWTKs66TYw==}
+ peerDependencies:
+ react: ^18.0.0
+ react-dom: ^18.0.0
+ dependencies:
+ '@radix-ui/react-dialog': 1.0.0(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ command-score: 0.1.2
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ transitivePeerDependencies:
+ - '@types/react'
+ dev: false
+
+ /color-convert@1.9.3:
+ resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
+ dependencies:
+ color-name: 1.1.3
+
+ /color-convert@2.0.1:
+ resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+ engines: {node: '>=7.0.0'}
+ dependencies:
+ color-name: 1.1.4
+
+ /color-name@1.1.3:
+ resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
+
+ /color-name@1.1.4:
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+ /color-string@1.9.1:
+ resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==}
+ dependencies:
+ color-name: 1.1.4
+ simple-swizzle: 0.2.2
+ dev: false
+
+ /color@4.2.3:
+ resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==}
+ engines: {node: '>=12.5.0'}
+ dependencies:
+ color-convert: 2.0.1
+ color-string: 1.9.1
+ dev: false
+
+ /comma-separated-tokens@2.0.3:
+ resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==}
+ dev: false
+
+ /command-score@0.1.2:
+ resolution: {integrity: sha512-VtDvQpIJBvBatnONUsPzXYFVKQQAhuf3XTNOAsdBxCNO/QCtUUd8LSgjn0GVarBkCad6aJCZfXgrjYbl/KRr7w==}
+ dev: false
+
+ /commander@10.0.1:
+ resolution: {integrity: sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==}
+ engines: {node: '>=14'}
+ dev: false
+
+ /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'}
+ dev: false
+
+ /commander@8.3.0:
+ resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==}
+ engines: {node: '>= 12'}
+ dev: false
+
+ /common-path-prefix@3.0.0:
+ resolution: {integrity: sha512-QE33hToZseCH3jS0qN96O/bSh3kaw/h+Tq7ngyY9eWDUnTlTNUyqfqvCXioLe5Na5jFsL78ra/wuBU4iuEgd4w==}
+ dev: false
+
+ /common-tags@1.8.2:
+ resolution: {integrity: sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA==}
+ engines: {node: '>=4.0.0'}
+
+ /commondir@1.0.1:
+ resolution: {integrity: sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==}
+ dev: false
+
+ /concat-map@0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+ /config-chain@1.1.13:
+ resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==}
+ dependencies:
+ ini: 1.3.8
+ proto-list: 1.2.4
+ dev: false
+
+ /convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+ /cookie@0.5.0:
+ resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
+ engines: {node: '>= 0.6'}
+ dev: false
+
+ /copy-anything@3.0.5:
+ resolution: {integrity: sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==}
+ engines: {node: '>=12.13'}
+ dependencies:
+ is-what: 4.1.16
+ dev: false
+
+ /core-js-compat@3.35.1:
+ resolution: {integrity: sha512-sftHa5qUJY3rs9Zht1WEnmkvXputCyDBczPnr7QDgL8n3qrF3CMXY4VPSYtOLLiOUJcah2WNXREd48iOl6mQIw==}
+ dependencies:
+ browserslist: 4.22.2
+
+ /cose-base@1.0.3:
+ resolution: {integrity: sha512-s9whTXInMSgAp/NVXVNuVxVKzGH2qck3aQlVHxDCdAEPgtMKwc4Wq6/QKhgdEdgbLSi9rBTAcPoRa6JpiG4ksg==}
+ dependencies:
+ layout-base: 1.0.2
+ dev: false
+
+ /cross-spawn@5.1.0:
+ resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==}
+ dependencies:
+ lru-cache: 4.1.5
+ shebang-command: 1.2.0
+ which: 1.3.1
+ dev: false
+
+ /cross-spawn@7.0.3:
+ resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
+ engines: {node: '>= 8'}
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+
+ /crypto-random-string@2.0.0:
+ resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==}
+ engines: {node: '>=8'}
+
+ /cssesc@3.0.0:
+ resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ /csstype@3.1.3:
+ resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
+ /cytoscape-cose-bilkent@4.1.0(cytoscape@3.28.1):
+ resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==}
+ peerDependencies:
+ cytoscape: ^3.2.0
+ dependencies:
+ cose-base: 1.0.3
+ cytoscape: 3.28.1
+ dev: false
+
+ /cytoscape@3.28.1:
+ resolution: {integrity: sha512-xyItz4O/4zp9/239wCcH8ZcFuuZooEeF8KHRmzjDfGdXsj3OG9MFSMA0pJE0uX3uCN/ygof6hHf4L7lst+JaDg==}
+ engines: {node: '>=0.10'}
+ dependencies:
+ heap: 0.2.7
+ lodash: 4.17.21
+ dev: false
+
+ /d3-array@2.12.1:
+ resolution: {integrity: sha512-B0ErZK/66mHtEsR1TkPEEkwdy+WDesimkM5gpZr5Dsg54BiTA5RXtYW5qTLIAcekaS9xfZrzBLF/OAkB3Qn1YQ==}
+ dependencies:
+ internmap: 1.0.1
+ dev: false
+
+ /d3-array@3.2.4:
+ resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==}
+ engines: {node: '>=12'}
+ dependencies:
+ internmap: 2.0.3
+ dev: false
+
+ /d3-axis@3.0.0:
+ resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==}
+ engines: {node: '>=12'}
+ dev: false
+
+ /d3-brush@3.0.0:
+ resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==}
+ engines: {node: '>=12'}
+ dependencies:
+ d3-dispatch: 3.0.1
+ d3-drag: 3.0.0
+ d3-interpolate: 3.0.1
+ d3-selection: 3.0.0
+ d3-transition: 3.0.1(d3-selection@3.0.0)
+ dev: false
+
+ /d3-chord@3.0.1:
+ resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==}
+ engines: {node: '>=12'}
+ dependencies:
+ d3-path: 3.1.0
+ dev: false
+
+ /d3-color@3.1.0:
+ resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
+ engines: {node: '>=12'}
+ dev: false
+
+ /d3-contour@4.0.2:
+ resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==}
+ engines: {node: '>=12'}
+ dependencies:
+ d3-array: 3.2.4
+ dev: false
+
+ /d3-delaunay@6.0.4:
+ resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==}
+ engines: {node: '>=12'}
+ dependencies:
+ delaunator: 5.0.1
+ dev: false
+
+ /d3-dispatch@3.0.1:
+ resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==}
+ engines: {node: '>=12'}
+ dev: false
+
+ /d3-drag@3.0.0:
+ resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==}
+ engines: {node: '>=12'}
+ dependencies:
+ d3-dispatch: 3.0.1
+ d3-selection: 3.0.0
+ dev: false
+
+ /d3-dsv@3.0.1:
+ resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==}
+ engines: {node: '>=12'}
+ hasBin: true
+ dependencies:
+ commander: 7.2.0
+ iconv-lite: 0.6.3
+ rw: 1.3.3
+ dev: false
+
+ /d3-ease@3.0.1:
+ resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
+ engines: {node: '>=12'}
+ dev: false
+
+ /d3-fetch@3.0.1:
+ resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==}
+ engines: {node: '>=12'}
+ dependencies:
+ d3-dsv: 3.0.1
+ dev: false
+
+ /d3-force@3.0.0:
+ resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==}
+ engines: {node: '>=12'}
+ dependencies:
+ d3-dispatch: 3.0.1
+ d3-quadtree: 3.0.1
+ d3-timer: 3.0.1
+ dev: false
+
+ /d3-format@3.1.0:
+ resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==}
+ engines: {node: '>=12'}
+ dev: false
+
+ /d3-geo@3.1.1:
+ resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==}
+ engines: {node: '>=12'}
+ dependencies:
+ d3-array: 3.2.4
+ dev: false
+
+ /d3-hierarchy@3.1.2:
+ resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==}
+ engines: {node: '>=12'}
+ dev: false
+
+ /d3-interpolate@3.0.1:
+ resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
+ engines: {node: '>=12'}
+ dependencies:
+ d3-color: 3.1.0
+ dev: false
+
+ /d3-path@1.0.9:
+ resolution: {integrity: sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==}
+ dev: false
+
+ /d3-path@3.1.0:
+ resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==}
+ engines: {node: '>=12'}
+ dev: false
+
+ /d3-polygon@3.0.1:
+ resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==}
+ engines: {node: '>=12'}
+ dev: false
+
+ /d3-quadtree@3.0.1:
+ resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==}
+ engines: {node: '>=12'}
+ dev: false
+
+ /d3-random@3.0.1:
+ resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==}
+ engines: {node: '>=12'}
+ dev: false
+
+ /d3-sankey@0.12.3:
+ resolution: {integrity: sha512-nQhsBRmM19Ax5xEIPLMY9ZmJ/cDvd1BG3UVvt5h3WRxKg5zGRbvnteTyWAbzeSvlh3tW7ZEmq4VwR5mB3tutmQ==}
+ dependencies:
+ d3-array: 2.12.1
+ d3-shape: 1.3.7
+ dev: false
+
+ /d3-scale-chromatic@3.1.0:
+ resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==}
+ engines: {node: '>=12'}
+ dependencies:
+ d3-color: 3.1.0
+ d3-interpolate: 3.0.1
+ dev: false
+
+ /d3-scale@4.0.2:
+ resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==}
+ engines: {node: '>=12'}
+ dependencies:
+ d3-array: 3.2.4
+ d3-format: 3.1.0
+ d3-interpolate: 3.0.1
+ d3-time: 3.1.0
+ d3-time-format: 4.1.0
+ dev: false
+
+ /d3-selection@3.0.0:
+ resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==}
+ engines: {node: '>=12'}
+ dev: false
+
+ /d3-shape@1.3.7:
+ resolution: {integrity: sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==}
+ dependencies:
+ d3-path: 1.0.9
+ dev: false
+
+ /d3-shape@3.2.0:
+ resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==}
+ engines: {node: '>=12'}
+ dependencies:
+ d3-path: 3.1.0
+ dev: false
+
+ /d3-time-format@4.1.0:
+ resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==}
+ engines: {node: '>=12'}
+ dependencies:
+ d3-time: 3.1.0
+ dev: false
+
+ /d3-time@3.1.0:
+ resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==}
+ engines: {node: '>=12'}
+ dependencies:
+ d3-array: 3.2.4
+ dev: false
+
+ /d3-timer@3.0.1:
+ resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
+ engines: {node: '>=12'}
+ dev: false
+
+ /d3-transition@3.0.1(d3-selection@3.0.0):
+ resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==}
+ engines: {node: '>=12'}
+ peerDependencies:
+ d3-selection: 2 - 3
+ dependencies:
+ d3-color: 3.1.0
+ d3-dispatch: 3.0.1
+ d3-ease: 3.0.1
+ d3-interpolate: 3.0.1
+ d3-selection: 3.0.0
+ d3-timer: 3.0.1
+ dev: false
+
+ /d3-zoom@3.0.0:
+ resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==}
+ engines: {node: '>=12'}
+ dependencies:
+ d3-dispatch: 3.0.1
+ d3-drag: 3.0.0
+ d3-interpolate: 3.0.1
+ d3-selection: 3.0.0
+ d3-transition: 3.0.1(d3-selection@3.0.0)
+ dev: false
+
+ /d3@7.9.0:
+ resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==}
+ engines: {node: '>=12'}
+ dependencies:
+ d3-array: 3.2.4
+ d3-axis: 3.0.0
+ d3-brush: 3.0.0
+ d3-chord: 3.0.1
+ d3-color: 3.1.0
+ d3-contour: 4.0.2
+ d3-delaunay: 6.0.4
+ d3-dispatch: 3.0.1
+ d3-drag: 3.0.0
+ d3-dsv: 3.0.1
+ d3-ease: 3.0.1
+ d3-fetch: 3.0.1
+ d3-force: 3.0.0
+ d3-format: 3.1.0
+ d3-geo: 3.1.1
+ d3-hierarchy: 3.1.2
+ d3-interpolate: 3.0.1
+ d3-path: 3.1.0
+ d3-polygon: 3.0.1
+ d3-quadtree: 3.0.1
+ d3-random: 3.0.1
+ d3-scale: 4.0.2
+ d3-scale-chromatic: 3.1.0
+ d3-selection: 3.0.0
+ d3-shape: 3.2.0
+ d3-time: 3.1.0
+ d3-time-format: 4.1.0
+ d3-timer: 3.0.1
+ d3-transition: 3.0.1(d3-selection@3.0.0)
+ d3-zoom: 3.0.0
+ dev: false
+
+ /dagre-d3-es@7.0.10:
+ resolution: {integrity: sha512-qTCQmEhcynucuaZgY5/+ti3X/rnszKZhEQH/ZdWdtP1tA/y3VoHJzcVrO9pjjJCNpigfscAtoUB5ONcd2wNn0A==}
+ dependencies:
+ d3: 7.9.0
+ lodash-es: 4.17.21
+ dev: false
+
+ /damerau-levenshtein@1.0.8:
+ resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
+ dev: true
+
+ /date-fns@3.3.1:
+ resolution: {integrity: sha512-y8e109LYGgoQDveiEBD3DYXKba1jWf5BA8YU1FL5Tvm0BTdEfy54WLCwnuYWZNnzzvALy/QQ4Hov+Q9RVRv+Zw==}
+ dev: false
+
+ /dayjs@1.11.10:
+ resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
+ dev: false
+
+ /debug@3.2.7:
+ resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+ 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
+ dependencies:
+ ms: 2.1.2
+
+ /decode-named-character-reference@1.0.2:
+ resolution: {integrity: sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg==}
+ dependencies:
+ character-entities: 2.0.2
+ dev: false
+
+ /decompress-response@6.0.0:
+ resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
+ engines: {node: '>=10'}
+ dependencies:
+ mimic-response: 3.1.0
+ dev: false
+
+ /deep-extend@0.6.0:
+ resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==}
+ engines: {node: '>=4.0.0'}
+ dev: false
+
+ /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.1:
+ resolution: {integrity: sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ get-intrinsic: 1.2.2
+ gopd: 1.0.1
+ has-property-descriptors: 1.0.1
+
+ /define-properties@1.2.1:
+ resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ define-data-property: 1.1.1
+ has-property-descriptors: 1.0.1
+ object-keys: 1.1.1
+
+ /del@4.1.1:
+ resolution: {integrity: sha512-QwGuEUouP2kVwQenAsOof5Fv8K9t3D8Ca8NxcXKrIpEHjTXK5J2nXLdP+ALI1cgv8wj7KuwBhTwBkOZSJKM5XQ==}
+ engines: {node: '>=6'}
+ dependencies:
+ '@types/glob': 7.2.0
+ globby: 6.1.0
+ is-path-cwd: 2.2.0
+ is-path-in-cwd: 2.1.0
+ p-map: 2.1.0
+ pify: 4.0.1
+ rimraf: 2.7.1
+ dev: false
+
+ /delaunator@5.0.1:
+ resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==}
+ dependencies:
+ robust-predicates: 3.0.2
+ dev: false
+
+ /dequal@2.0.3:
+ resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
+ engines: {node: '>=6'}
+
+ /detect-libc@2.0.2:
+ resolution: {integrity: sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==}
+ engines: {node: '>=8'}
+ dev: false
+
+ /detect-node-es@1.1.0:
+ resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==}
+ dev: false
+
+ /devlop@1.1.0:
+ resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
+ dependencies:
+ dequal: 2.0.3
+ dev: false
+
+ /didyoumean@1.2.2:
+ resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
+
+ /diff@5.2.0:
+ resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==}
+ engines: {node: '>=0.3.1'}
+ dev: false
+
+ /dir-glob@3.0.1:
+ resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
+ engines: {node: '>=8'}
+ dependencies:
+ path-type: 4.0.0
+
+ /dlv@1.1.3:
+ resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==}
+
+ /doctrine@2.1.0:
+ resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ esutils: 2.0.3
+ dev: true
+
+ /doctrine@3.0.0:
+ resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
+ engines: {node: '>=6.0.0'}
+ dependencies:
+ esutils: 2.0.3
+ dev: true
+
+ /dom-serializer@2.0.0:
+ resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
+ dependencies:
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+ entities: 4.5.0
+ dev: false
+
+ /domelementtype@2.3.0:
+ resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
+ dev: false
+
+ /domhandler@5.0.3:
+ resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
+ engines: {node: '>= 4'}
+ dependencies:
+ domelementtype: 2.3.0
+ dev: false
+
+ /dompurify@3.0.9:
+ resolution: {integrity: sha512-uyb4NDIvQ3hRn6NiC+SIFaP4mJ/MdXlvtunaqK9Bn6dD3RuB/1S/gasEjDHD8eiaqdSael2vBv+hOs7Y+jhYOQ==}
+ dev: false
+
+ /domutils@3.1.0:
+ resolution: {integrity: sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==}
+ dependencies:
+ dom-serializer: 2.0.0
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+ dev: false
+
+ /eastasianwidth@0.2.0:
+ resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+
+ /ecdsa-sig-formatter@1.0.11:
+ resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
+ dependencies:
+ safe-buffer: 5.2.1
+ dev: false
+
+ /editorconfig@1.0.4:
+ resolution: {integrity: sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==}
+ engines: {node: '>=14'}
+ hasBin: true
+ dependencies:
+ '@one-ini/wasm': 0.1.1
+ commander: 10.0.1
+ minimatch: 9.0.1
+ semver: 7.5.4
+ dev: false
+
+ /ejs@3.1.9:
+ resolution: {integrity: sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==}
+ engines: {node: '>=0.10.0'}
+ hasBin: true
+ dependencies:
+ jake: 10.8.7
+
+ /electron-to-chromium@1.4.640:
+ resolution: {integrity: sha512-z/6oZ/Muqk4BaE7P69bXhUhpJbUM9ZJeka43ZwxsDshKtePns4mhBlh8bU5+yrnOnz3fhG82XLzGUXazOmsWnA==}
+
+ /electron-to-chromium@1.4.655:
+ resolution: {integrity: sha512-2yszojF7vIZ68adIOvzV4bku8OZad9w5H9xF3ZAMZjPuOjBarlflUkjN6DggdV+L71WZuKUfKUhov/34+G5QHg==}
+ dev: false
+
+ /elkjs@0.9.2:
+ resolution: {integrity: sha512-2Y/RaA1pdgSHpY0YG4TYuYCD2wh97CRvu22eLG3Kz0pgQ/6KbIFTxsTnDc4MH/6hFlg2L/9qXrDMG0nMjP63iw==}
+ dev: false
+
+ /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'}
+ dev: false
+
+ /end-of-stream@1.4.4:
+ resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
+ dependencies:
+ once: 1.4.0
+ dev: false
+
+ /enhanced-resolve@5.15.0:
+ resolution: {integrity: sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg==}
+ engines: {node: '>=10.13.0'}
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.2.1
+
+ /entities@4.5.0:
+ resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+ engines: {node: '>=0.12'}
+ dev: false
+
+ /es-abstract@1.22.3:
+ resolution: {integrity: sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ array-buffer-byte-length: 1.0.0
+ arraybuffer.prototype.slice: 1.0.2
+ available-typed-arrays: 1.0.5
+ call-bind: 1.0.5
+ es-set-tostringtag: 2.0.2
+ es-to-primitive: 1.2.1
+ function.prototype.name: 1.1.6
+ get-intrinsic: 1.2.2
+ get-symbol-description: 1.0.0
+ globalthis: 1.0.3
+ gopd: 1.0.1
+ has-property-descriptors: 1.0.1
+ has-proto: 1.0.1
+ has-symbols: 1.0.3
+ hasown: 2.0.0
+ internal-slot: 1.0.6
+ is-array-buffer: 3.0.2
+ is-callable: 1.2.7
+ is-negative-zero: 2.0.2
+ is-regex: 1.1.4
+ is-shared-array-buffer: 1.0.2
+ is-string: 1.0.7
+ is-typed-array: 1.1.12
+ is-weakref: 1.0.2
+ object-inspect: 1.13.1
+ object-keys: 1.1.1
+ object.assign: 4.1.5
+ regexp.prototype.flags: 1.5.1
+ safe-array-concat: 1.1.0
+ safe-regex-test: 1.0.2
+ string.prototype.trim: 1.2.8
+ string.prototype.trimend: 1.0.7
+ string.prototype.trimstart: 1.0.7
+ typed-array-buffer: 1.0.0
+ typed-array-byte-length: 1.0.0
+ typed-array-byte-offset: 1.0.0
+ typed-array-length: 1.0.4
+ unbox-primitive: 1.0.2
+ which-typed-array: 1.1.13
+
+ /es-iterator-helpers@1.0.15:
+ resolution: {integrity: sha512-GhoY8uYqd6iwUl2kgjTm4CZAf6oo5mHK7BPqx3rKgx893YSsy0LGHV6gfqqQvZt/8xM8xeOnfXBCfqclMKkJ5g==}
+ dependencies:
+ asynciterator.prototype: 1.0.0
+ call-bind: 1.0.5
+ define-properties: 1.2.1
+ es-abstract: 1.22.3
+ es-set-tostringtag: 2.0.2
+ function-bind: 1.1.2
+ get-intrinsic: 1.2.2
+ globalthis: 1.0.3
+ has-property-descriptors: 1.0.1
+ has-proto: 1.0.1
+ has-symbols: 1.0.3
+ internal-slot: 1.0.6
+ iterator.prototype: 1.1.2
+ safe-array-concat: 1.1.0
+ dev: true
+
+ /es-module-lexer@1.4.1:
+ resolution: {integrity: sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w==}
+ dev: false
+
+ /es-set-tostringtag@2.0.2:
+ resolution: {integrity: sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ get-intrinsic: 1.2.2
+ has-tostringtag: 1.0.0
+ hasown: 2.0.0
+
+ /es-shim-unscopables@1.0.2:
+ resolution: {integrity: sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==}
+ dependencies:
+ hasown: 2.0.0
+ dev: true
+
+ /es-to-primitive@1.2.1:
+ resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ is-callable: 1.2.7
+ is-date-object: 1.0.5
+ is-symbol: 1.0.4
+
+ /esbuild@0.19.12:
+ resolution: {integrity: sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==}
+ engines: {node: '>=12'}
+ hasBin: true
+ requiresBuild: true
+ optionalDependencies:
+ '@esbuild/aix-ppc64': 0.19.12
+ '@esbuild/android-arm': 0.19.12
+ '@esbuild/android-arm64': 0.19.12
+ '@esbuild/android-x64': 0.19.12
+ '@esbuild/darwin-arm64': 0.19.12
+ '@esbuild/darwin-x64': 0.19.12
+ '@esbuild/freebsd-arm64': 0.19.12
+ '@esbuild/freebsd-x64': 0.19.12
+ '@esbuild/linux-arm': 0.19.12
+ '@esbuild/linux-arm64': 0.19.12
+ '@esbuild/linux-ia32': 0.19.12
+ '@esbuild/linux-loong64': 0.19.12
+ '@esbuild/linux-mips64el': 0.19.12
+ '@esbuild/linux-ppc64': 0.19.12
+ '@esbuild/linux-riscv64': 0.19.12
+ '@esbuild/linux-s390x': 0.19.12
+ '@esbuild/linux-x64': 0.19.12
+ '@esbuild/netbsd-x64': 0.19.12
+ '@esbuild/openbsd-x64': 0.19.12
+ '@esbuild/sunos-x64': 0.19.12
+ '@esbuild/win32-arm64': 0.19.12
+ '@esbuild/win32-ia32': 0.19.12
+ '@esbuild/win32-x64': 0.19.12
+ dev: true
+
+ /escalade@3.1.1:
+ resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
+ engines: {node: '>=6'}
+
+ /escape-string-regexp@1.0.5:
+ resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
+ engines: {node: '>=0.8.0'}
+
+ /escape-string-regexp@4.0.0:
+ resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+ engines: {node: '>=10'}
+ dev: true
+
+ /escape-string-regexp@5.0.0:
+ resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==}
+ engines: {node: '>=12'}
+ dev: false
+
+ /eslint-config-next@14.1.0(eslint@8.56.0)(typescript@5.3.3):
+ resolution: {integrity: sha512-SBX2ed7DoRFXC6CQSLc/SbLY9Ut6HxNB2wPTcoIWjUMd7aF7O/SIE7111L8FdZ9TXsNV4pulUDnfthpyPtbFUg==}
+ peerDependencies:
+ eslint: ^7.23.0 || ^8.0.0
+ typescript: '>=3.3.1'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ dependencies:
+ '@next/eslint-plugin-next': 14.1.0
+ '@rushstack/eslint-patch': 1.7.0
+ '@typescript-eslint/parser': 6.19.0(eslint@8.56.0)(typescript@5.3.3)
+ eslint: 8.56.0
+ eslint-import-resolver-node: 0.3.9
+ eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.19.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.56.0)
+ eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.19.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0)
+ eslint-plugin-jsx-a11y: 6.8.0(eslint@8.56.0)
+ eslint-plugin-react: 7.33.2(eslint@8.56.0)
+ eslint-plugin-react-hooks: 4.6.0(eslint@8.56.0)
+ typescript: 5.3.3
+ transitivePeerDependencies:
+ - eslint-import-resolver-webpack
+ - supports-color
+ dev: true
+
+ /eslint-import-resolver-node@0.3.9:
+ resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==}
+ dependencies:
+ debug: 3.2.7
+ is-core-module: 2.13.1
+ resolve: 1.22.8
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@6.19.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.56.0):
+ resolution: {integrity: sha512-xgdptdoi5W3niYeuQxKmzVDTATvLYqhpwmykwsh7f6HIOStGWEIL9iqZgQDF9u9OEzrRwR8no5q2VT+bjAujTg==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+ peerDependencies:
+ eslint: '*'
+ eslint-plugin-import: '*'
+ dependencies:
+ debug: 4.3.4
+ enhanced-resolve: 5.15.0
+ eslint: 8.56.0
+ eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.19.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0)
+ eslint-plugin-import: 2.29.1(@typescript-eslint/parser@6.19.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0)
+ fast-glob: 3.3.2
+ get-tsconfig: 4.7.2
+ is-core-module: 2.13.1
+ is-glob: 4.0.3
+ transitivePeerDependencies:
+ - '@typescript-eslint/parser'
+ - eslint-import-resolver-node
+ - eslint-import-resolver-webpack
+ - supports-color
+ dev: true
+
+ /eslint-module-utils@2.8.0(@typescript-eslint/parser@6.19.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.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
+ dependencies:
+ '@typescript-eslint/parser': 6.19.0(eslint@8.56.0)(typescript@5.3.3)
+ debug: 3.2.7
+ eslint: 8.56.0
+ eslint-import-resolver-node: 0.3.9
+ eslint-import-resolver-typescript: 3.6.1(@typescript-eslint/parser@6.19.0)(eslint-import-resolver-node@0.3.9)(eslint-plugin-import@2.29.1)(eslint@8.56.0)
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /eslint-plugin-import@2.29.1(@typescript-eslint/parser@6.19.0)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0):
+ resolution: {integrity: sha512-BbPC0cuExzhiMo4Ff1BTVwHpjjv28C5R+btTOGaCRC7UEz801up0JadwkeSk5Ued6TG34uaczuVuH6qyy5YUxw==}
+ engines: {node: '>=4'}
+ peerDependencies:
+ '@typescript-eslint/parser': '*'
+ eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8
+ peerDependenciesMeta:
+ '@typescript-eslint/parser':
+ optional: true
+ dependencies:
+ '@typescript-eslint/parser': 6.19.0(eslint@8.56.0)(typescript@5.3.3)
+ 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.56.0
+ eslint-import-resolver-node: 0.3.9
+ eslint-module-utils: 2.8.0(@typescript-eslint/parser@6.19.0)(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1)(eslint@8.56.0)
+ hasown: 2.0.0
+ is-core-module: 2.13.1
+ is-glob: 4.0.3
+ minimatch: 3.1.2
+ object.fromentries: 2.0.7
+ object.groupby: 1.0.1
+ object.values: 1.1.7
+ semver: 6.3.1
+ tsconfig-paths: 3.15.0
+ transitivePeerDependencies:
+ - eslint-import-resolver-typescript
+ - eslint-import-resolver-webpack
+ - supports-color
+ dev: true
+
+ /eslint-plugin-jsx-a11y@6.8.0(eslint@8.56.0):
+ resolution: {integrity: sha512-Hdh937BS3KdwwbBaKd5+PLCOmYY6U4f2h9Z2ktwtNKvIdIEu137rjYbcb9ApSbVJfWxANNuiKTD/9tOKjK9qOA==}
+ engines: {node: '>=4.0'}
+ peerDependencies:
+ eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
+ dependencies:
+ '@babel/runtime': 7.23.8
+ aria-query: 5.3.0
+ array-includes: 3.1.7
+ array.prototype.flatmap: 1.3.2
+ ast-types-flow: 0.0.8
+ axe-core: 4.7.0
+ axobject-query: 3.2.1
+ damerau-levenshtein: 1.0.8
+ emoji-regex: 9.2.2
+ es-iterator-helpers: 1.0.15
+ eslint: 8.56.0
+ hasown: 2.0.0
+ jsx-ast-utils: 3.3.5
+ language-tags: 1.0.9
+ minimatch: 3.1.2
+ object.entries: 1.1.7
+ object.fromentries: 2.0.7
+ dev: true
+
+ /eslint-plugin-react-hooks@4.6.0(eslint@8.56.0):
+ resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0
+ dependencies:
+ eslint: 8.56.0
+ dev: true
+
+ /eslint-plugin-react@7.33.2(eslint@8.56.0):
+ resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==}
+ engines: {node: '>=4'}
+ peerDependencies:
+ eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8
+ dependencies:
+ array-includes: 3.1.7
+ array.prototype.flatmap: 1.3.2
+ array.prototype.tosorted: 1.1.2
+ doctrine: 2.1.0
+ es-iterator-helpers: 1.0.15
+ eslint: 8.56.0
+ estraverse: 5.3.0
+ jsx-ast-utils: 3.3.5
+ minimatch: 3.1.2
+ object.entries: 1.1.7
+ object.fromentries: 2.0.7
+ object.hasown: 1.1.3
+ object.values: 1.1.7
+ prop-types: 15.8.1
+ resolve: 2.0.0-next.5
+ semver: 6.3.1
+ string.prototype.matchall: 4.0.10
+ dev: true
+
+ /eslint-scope@5.1.1:
+ resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==}
+ engines: {node: '>=8.0.0'}
+ dependencies:
+ esrecurse: 4.3.0
+ estraverse: 4.3.0
+ dev: false
+
+ /eslint-scope@7.2.2:
+ resolution: {integrity: sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ dependencies:
+ esrecurse: 4.3.0
+ estraverse: 5.3.0
+ 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@8.56.0:
+ resolution: {integrity: sha512-Go19xM6T9puCOWntie1/P997aXxFsOi37JIHRWI514Hc6ZnaHGKY9xFhrU65RT6CcBEzZoGG1e6Nq+DT04ZtZQ==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ hasBin: true
+ dependencies:
+ '@eslint-community/eslint-utils': 4.4.0(eslint@8.56.0)
+ '@eslint-community/regexpp': 4.10.0
+ '@eslint/eslintrc': 2.1.4
+ '@eslint/js': 8.56.0
+ '@humanwhocodes/config-array': 0.11.14
+ '@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
+ doctrine: 3.0.0
+ escape-string-regexp: 4.0.0
+ eslint-scope: 7.2.2
+ eslint-visitor-keys: 3.4.3
+ espree: 9.6.1
+ esquery: 1.5.0
+ esutils: 2.0.3
+ fast-deep-equal: 3.1.3
+ file-entry-cache: 6.0.1
+ find-up: 5.0.0
+ glob-parent: 6.0.2
+ globals: 13.24.0
+ graphemer: 1.4.0
+ ignore: 5.3.0
+ imurmurhash: 0.1.4
+ is-glob: 4.0.3
+ is-path-inside: 3.0.3
+ js-yaml: 4.1.0
+ json-stable-stringify-without-jsonify: 1.0.1
+ levn: 0.4.1
+ lodash.merge: 4.6.2
+ minimatch: 3.1.2
+ natural-compare: 1.4.0
+ optionator: 0.9.3
+ strip-ansi: 6.0.1
+ text-table: 0.2.0
+ 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}
+ dependencies:
+ acorn: 8.11.3
+ acorn-jsx: 5.3.2(acorn@8.11.3)
+ 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: false
+
+ /esquery@1.5.0:
+ resolution: {integrity: sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==}
+ engines: {node: '>=0.10'}
+ dependencies:
+ estraverse: 5.3.0
+ dev: true
+
+ /esrecurse@4.3.0:
+ resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+ engines: {node: '>=4.0'}
+ dependencies:
+ estraverse: 5.3.0
+
+ /estraverse@4.3.0:
+ resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==}
+ engines: {node: '>=4.0'}
+ dev: false
+
+ /estraverse@5.3.0:
+ resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+ engines: {node: '>=4.0'}
+
+ /estree-util-attach-comments@2.1.1:
+ resolution: {integrity: sha512-+5Ba/xGGS6mnwFbXIuQiDPTbuTxuMCooq3arVv7gPZtYpjp+VXH/NkHAP35OOefPhNG/UGqU3vt/LTABwcHX0w==}
+ dependencies:
+ '@types/estree': 1.0.5
+ dev: false
+
+ /estree-util-build-jsx@2.2.2:
+ resolution: {integrity: sha512-m56vOXcOBuaF+Igpb9OPAy7f9w9OIkb5yhjsZuaPm7HoGi4oTOQi0h2+yZ+AtKklYFZ+rPC4n0wYCJCEU1ONqg==}
+ dependencies:
+ '@types/estree-jsx': 1.0.5
+ estree-util-is-identifier-name: 2.1.0
+ estree-walker: 3.0.3
+ dev: false
+
+ /estree-util-is-identifier-name@2.1.0:
+ resolution: {integrity: sha512-bEN9VHRyXAUOjkKVQVvArFym08BTWB0aJPppZZr0UNyAqWsLaVfAqP7hbaTJjzHifmB5ebnR8Wm7r7yGN/HonQ==}
+ dev: false
+
+ /estree-util-to-js@1.2.0:
+ resolution: {integrity: sha512-IzU74r1PK5IMMGZXUVZbmiu4A1uhiPgW5hm1GjcOfr4ZzHaMPpLNJjR7HjXiIOzi25nZDrgFTobHTkV5Q6ITjA==}
+ dependencies:
+ '@types/estree-jsx': 1.0.5
+ astring: 1.8.6
+ source-map: 0.7.4
+ dev: false
+
+ /estree-util-value-to-estree@1.3.0:
+ resolution: {integrity: sha512-Y+ughcF9jSUJvncXwqRageavjrNPAI+1M/L3BI3PyLp1nmgYTGUXU6t5z1Y7OWuThoDdhPME07bQU+d5LxdJqw==}
+ engines: {node: '>=12.0.0'}
+ dependencies:
+ is-plain-obj: 3.0.0
+ dev: false
+
+ /estree-util-visit@1.2.1:
+ resolution: {integrity: sha512-xbgqcrkIVbIG+lI/gzbvd9SGTJL4zqJKBFttUl5pP27KhAjtMKbX/mQXJ7qgyXpMgVy/zvpm0xoQQaGL8OloOw==}
+ dependencies:
+ '@types/estree-jsx': 1.0.5
+ '@types/unist': 2.0.10
+ dev: false
+
+ /estree-walker@1.0.1:
+ resolution: {integrity: sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==}
+
+ /estree-walker@3.0.3:
+ resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
+ dependencies:
+ '@types/estree': 1.0.5
+ dev: false
+
+ /esutils@2.0.3:
+ resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+ engines: {node: '>=0.10.0'}
+
+ /events@3.3.0:
+ resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==}
+ engines: {node: '>=0.8.x'}
+ dev: false
+
+ /execa@0.8.0:
+ resolution: {integrity: sha512-zDWS+Rb1E8BlqqhALSt9kUhss8Qq4nN3iof3gsOdyINksElaPyNBtKUMTR62qhvgVWR0CqCX7sdnKe4MnUbFEA==}
+ engines: {node: '>=4'}
+ dependencies:
+ cross-spawn: 5.1.0
+ get-stream: 3.0.0
+ is-stream: 1.1.0
+ npm-run-path: 2.0.2
+ p-finally: 1.0.0
+ signal-exit: 3.0.7
+ strip-eof: 1.0.0
+ dev: false
+
+ /expand-template@2.0.3:
+ resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==}
+ engines: {node: '>=6'}
+ dev: false
+
+ /extend-shallow@2.0.1:
+ resolution: {integrity: sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ is-extendable: 0.1.1
+ dev: false
+
+ /extend@3.0.2:
+ resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==}
+ dev: false
+
+ /fast-deep-equal@3.1.3:
+ resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+ /fast-fifo@1.3.2:
+ resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==}
+ dev: false
+
+ /fast-glob@3.3.2:
+ resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
+ engines: {node: '>=8.6.0'}
+ 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
+
+ /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-xml-parser@4.2.5:
+ resolution: {integrity: sha512-B9/wizE4WngqQftFPmdaMYlXoJlJOYxGQOanC77fq9k8+Z0v5dDSVh+3glErdIROP//s/jgb7ZuxKfB8nVyo0g==}
+ hasBin: true
+ dependencies:
+ strnum: 1.0.5
+ dev: false
+
+ /fastq@1.16.0:
+ resolution: {integrity: sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==}
+ dependencies:
+ reusify: 1.0.4
+
+ /file-entry-cache@6.0.1:
+ resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
+ engines: {node: ^10.12.0 || >=12.0.0}
+ dependencies:
+ flat-cache: 3.2.0
+ dev: true
+
+ /filelist@1.0.4:
+ resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
+ dependencies:
+ minimatch: 5.1.6
+
+ /fill-range@7.0.1:
+ resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
+ engines: {node: '>=8'}
+ dependencies:
+ to-regex-range: 5.0.1
+
+ /find-cache-dir@3.3.2:
+ resolution: {integrity: sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig==}
+ engines: {node: '>=8'}
+ dependencies:
+ commondir: 1.0.1
+ make-dir: 3.1.0
+ pkg-dir: 4.2.0
+ dev: false
+
+ /find-cache-dir@4.0.0:
+ resolution: {integrity: sha512-9ZonPT4ZAK4a+1pUPVPZJapbi7O5qbbJPdYw/NOQWZZbVLdDTYM3A4R9z/DpAM08IDaFGsvPgiGZ82WEwUDWjg==}
+ engines: {node: '>=14.16'}
+ dependencies:
+ common-path-prefix: 3.0.0
+ pkg-dir: 7.0.0
+ dev: false
+
+ /find-up@4.1.0:
+ resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
+ engines: {node: '>=8'}
+ dependencies:
+ locate-path: 5.0.0
+ path-exists: 4.0.0
+ dev: false
+
+ /find-up@5.0.0:
+ resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+ engines: {node: '>=10'}
+ dependencies:
+ locate-path: 6.0.0
+ path-exists: 4.0.0
+ dev: true
+
+ /find-up@6.3.0:
+ resolution: {integrity: sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ dependencies:
+ locate-path: 7.2.0
+ path-exists: 5.0.0
+ dev: false
+
+ /flat-cache@3.2.0:
+ resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==}
+ engines: {node: ^10.12.0 || >=12.0.0}
+ dependencies:
+ flatted: 3.2.9
+ keyv: 4.5.4
+ rimraf: 3.0.2
+ dev: true
+
+ /flatted@3.2.9:
+ resolution: {integrity: sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==}
+ dev: true
+
+ /for-each@0.3.3:
+ resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
+ dependencies:
+ is-callable: 1.2.7
+
+ /foreground-child@3.1.1:
+ resolution: {integrity: sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==}
+ engines: {node: '>=14'}
+ dependencies:
+ cross-spawn: 7.0.3
+ signal-exit: 4.1.0
+
+ /fraction.js@4.3.7:
+ resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
+ dev: true
+
+ /framer-motion@11.0.3(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-6x2poQpIWBdbZwLd73w6cKZ1I9IEPIU94C6/Swp1Zt3LJ+sB5bPe1E2wC6EH5hSISXNkMJ4afH7AdwS7MrtkWw==}
+ peerDependencies:
+ react: ^18.0.0
+ react-dom: ^18.0.0
+ peerDependenciesMeta:
+ react:
+ optional: true
+ react-dom:
+ optional: true
+ dependencies:
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ tslib: 2.6.2
+ optionalDependencies:
+ '@emotion/is-prop-valid': 0.8.8
+ dev: false
+
+ /fs-constants@1.0.0:
+ resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
+ dev: false
+
+ /fs-extra@9.1.0:
+ resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==}
+ engines: {node: '>=10'}
+ dependencies:
+ at-least-node: 1.0.0
+ graceful-fs: 4.2.11
+ jsonfile: 6.1.0
+ universalify: 2.0.1
+
+ /fs.realpath@1.0.0:
+ resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+
+ /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
+ optional: true
+
+ /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'}
+ dependencies:
+ call-bind: 1.0.5
+ define-properties: 1.2.1
+ es-abstract: 1.22.3
+ functions-have-names: 1.2.3
+
+ /functions-have-names@1.2.3:
+ resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
+
+ /geist@1.2.1(next@14.1.0):
+ resolution: {integrity: sha512-xCl7zWfnWqc+TbCG5qqyeT5tnVlOO4pSJsT3Ei59DN1SR4N2VlauF8Fv0D1pPFXGUJgu6RMoeZX+wsR4T9bMhg==}
+ peerDependencies:
+ next: ^13.2 || ^14
+ dependencies:
+ next: 14.1.0(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0)
+ dev: false
+
+ /gensync@1.0.0-beta.2:
+ resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+ engines: {node: '>=6.9.0'}
+
+ /get-intrinsic@1.2.2:
+ resolution: {integrity: sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==}
+ dependencies:
+ function-bind: 1.1.2
+ has-proto: 1.0.1
+ has-symbols: 1.0.3
+ hasown: 2.0.0
+
+ /get-nonce@1.0.1:
+ resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==}
+ engines: {node: '>=6'}
+ dev: false
+
+ /get-own-enumerable-property-symbols@3.0.2:
+ resolution: {integrity: sha512-I0UBV/XOz1XkIJHEUDMZAbzCThU/H8DxmSfmdGcKPnVhu2VfFqr34jr9777IyaTYvxjedWhqVIilEDsCdP5G6g==}
+
+ /get-stream@3.0.0:
+ resolution: {integrity: sha512-GlhdIUuVakc8SJ6kK0zAFbiGzRFzNnY4jUuEbV9UROo4Y+0Ny4fjvcZFVTeDA4odpFyOQzaw6hXukJSq/f28sQ==}
+ engines: {node: '>=4'}
+ dev: false
+
+ /get-symbol-description@1.0.0:
+ resolution: {integrity: sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.5
+ get-intrinsic: 1.2.2
+
+ /get-tsconfig@4.7.2:
+ resolution: {integrity: sha512-wuMsz4leaj5hbGgg4IvDU0bqJagpftG5l5cXIAvo8uZrqn0NJqwtfupTN00VnkQJPcIRrxYrm1Ue24btpCha2A==}
+ dependencies:
+ resolve-pkg-maps: 1.0.0
+ dev: true
+
+ /github-from-package@0.0.0:
+ resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==}
+ dev: false
+
+ /github-slugger@2.0.0:
+ resolution: {integrity: sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==}
+ dev: false
+
+ /glob-parent@5.1.2:
+ resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+ engines: {node: '>= 6'}
+ dependencies:
+ is-glob: 4.0.3
+
+ /glob-parent@6.0.2:
+ resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+ engines: {node: '>=10.13.0'}
+ dependencies:
+ is-glob: 4.0.3
+
+ /glob-to-regexp@0.4.1:
+ resolution: {integrity: sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==}
+
+ /glob@10.3.10:
+ resolution: {integrity: sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==}
+ engines: {node: '>=16 || 14 >=14.17'}
+ hasBin: true
+ dependencies:
+ foreground-child: 3.1.1
+ jackspeak: 2.3.6
+ minimatch: 9.0.3
+ minipass: 7.0.4
+ path-scurry: 1.10.1
+
+ /glob@7.2.3:
+ resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+ dependencies:
+ fs.realpath: 1.0.0
+ inflight: 1.0.6
+ inherits: 2.0.4
+ minimatch: 3.1.2
+ once: 1.4.0
+ path-is-absolute: 1.0.1
+
+ /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'}
+ dependencies:
+ type-fest: 0.20.2
+ dev: true
+
+ /globalthis@1.0.3:
+ resolution: {integrity: sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ define-properties: 1.2.1
+
+ /globby@11.1.0:
+ resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
+ engines: {node: '>=10'}
+ dependencies:
+ array-union: 2.1.0
+ dir-glob: 3.0.1
+ fast-glob: 3.3.2
+ ignore: 5.3.0
+ merge2: 1.4.1
+ slash: 3.0.0
+
+ /globby@6.1.0:
+ resolution: {integrity: sha512-KVbFv2TQtbzCoxAnfD6JcHZTYCzyliEaaeM/gH8qQdkKr5s0OP9scEgvdcngyk7AVdY6YVW/TJHd+lQ/Df3Daw==}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ array-union: 1.0.2
+ glob: 7.2.3
+ object-assign: 4.1.1
+ pify: 2.3.0
+ pinkie-promise: 2.0.1
+ dev: false
+
+ /gopd@1.0.1:
+ resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==}
+ dependencies:
+ get-intrinsic: 1.2.2
+
+ /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
+
+ /gray-matter@4.0.3:
+ resolution: {integrity: sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q==}
+ engines: {node: '>=6.0'}
+ dependencies:
+ js-yaml: 3.14.1
+ kind-of: 6.0.3
+ section-matter: 1.0.0
+ strip-bom-string: 1.0.0
+ dev: false
+
+ /has-bigints@1.0.2:
+ resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
+
+ /has-flag@2.0.0:
+ resolution: {integrity: sha512-P+1n3MnwjR/Epg9BBo1KT8qbye2g2Ou4sFumihwt6I4tsUX7jnLcX4BTOSKg/B1ZrIYMN9FcEnG4x5a7NB8Eng==}
+ engines: {node: '>=0.10.0'}
+ dev: false
+
+ /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.1:
+ resolution: {integrity: sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==}
+ dependencies:
+ get-intrinsic: 1.2.2
+
+ /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'}
+ dependencies:
+ has-symbols: 1.0.3
+
+ /hash-obj@4.0.0:
+ resolution: {integrity: sha512-FwO1BUVWkyHasWDW4S8o0ssQXjvyghLV2rfVhnN36b2bbcj45eGiuzdn9XOvOpjV3TKQD7Gm2BWNXdE9V4KKYg==}
+ engines: {node: '>=12'}
+ dependencies:
+ is-obj: 3.0.0
+ sort-keys: 5.0.0
+ type-fest: 1.4.0
+ dev: false
+
+ /hasown@2.0.0:
+ resolution: {integrity: sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ function-bind: 1.1.2
+
+ /hast-util-from-dom@5.0.0:
+ resolution: {integrity: sha512-d6235voAp/XR3Hh5uy7aGLbM3S4KamdW0WEgOaU1YoewnuYw4HXb5eRtv9g65m/RFGEfUY1Mw4UqCc5Y8L4Stg==}
+ dependencies:
+ '@types/hast': 3.0.4
+ hastscript: 8.0.0
+ web-namespaces: 2.0.1
+ dev: false
+
+ /hast-util-from-html-isomorphic@2.0.0:
+ resolution: {integrity: sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw==}
+ dependencies:
+ '@types/hast': 3.0.4
+ hast-util-from-dom: 5.0.0
+ hast-util-from-html: 2.0.1
+ unist-util-remove-position: 5.0.0
+ dev: false
+
+ /hast-util-from-html@2.0.1:
+ resolution: {integrity: sha512-RXQBLMl9kjKVNkJTIO6bZyb2n+cUH8LFaSSzo82jiLT6Tfc+Pt7VQCS+/h3YwG4jaNE2TA2sdJisGWR+aJrp0g==}
+ dependencies:
+ '@types/hast': 3.0.4
+ devlop: 1.1.0
+ hast-util-from-parse5: 8.0.1
+ parse5: 7.1.2
+ vfile: 6.0.1
+ vfile-message: 4.0.2
+ dev: false
+
+ /hast-util-from-parse5@8.0.1:
+ resolution: {integrity: sha512-Er/Iixbc7IEa7r/XLtuG52zoqn/b3Xng/w6aZQ0xGVxzhw5xUFxcRqdPzP6yFi/4HBYRaifaI5fQ1RH8n0ZeOQ==}
+ dependencies:
+ '@types/hast': 3.0.4
+ '@types/unist': 3.0.2
+ devlop: 1.1.0
+ hastscript: 8.0.0
+ property-information: 6.4.1
+ vfile: 6.0.1
+ vfile-location: 5.0.2
+ web-namespaces: 2.0.1
+ dev: false
+
+ /hast-util-is-element@3.0.0:
+ resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==}
+ dependencies:
+ '@types/hast': 3.0.4
+ dev: false
+
+ /hast-util-parse-selector@4.0.0:
+ resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==}
+ dependencies:
+ '@types/hast': 3.0.4
+ dev: false
+
+ /hast-util-raw@9.0.2:
+ resolution: {integrity: sha512-PldBy71wO9Uq1kyaMch9AHIghtQvIwxBUkv823pKmkTM3oV1JxtsTNYdevMxvUHqcnOAuO65JKU2+0NOxc2ksA==}
+ dependencies:
+ '@types/hast': 3.0.4
+ '@types/unist': 3.0.2
+ '@ungap/structured-clone': 1.2.0
+ hast-util-from-parse5: 8.0.1
+ hast-util-to-parse5: 8.0.0
+ html-void-elements: 3.0.0
+ mdast-util-to-hast: 13.1.0
+ parse5: 7.1.2
+ unist-util-position: 5.0.0
+ unist-util-visit: 5.0.0
+ vfile: 6.0.1
+ web-namespaces: 2.0.1
+ zwitch: 2.0.4
+ dev: false
+
+ /hast-util-to-estree@2.3.3:
+ resolution: {integrity: sha512-ihhPIUPxN0v0w6M5+IiAZZrn0LH2uZomeWwhn7uP7avZC6TE7lIiEh2yBMPr5+zi1aUCXq6VoYRgs2Bw9xmycQ==}
+ dependencies:
+ '@types/estree': 1.0.5
+ '@types/estree-jsx': 1.0.5
+ '@types/hast': 2.3.10
+ '@types/unist': 2.0.10
+ comma-separated-tokens: 2.0.3
+ estree-util-attach-comments: 2.1.1
+ estree-util-is-identifier-name: 2.1.0
+ hast-util-whitespace: 2.0.1
+ mdast-util-mdx-expression: 1.3.2
+ mdast-util-mdxjs-esm: 1.3.1
+ property-information: 6.4.1
+ space-separated-tokens: 2.0.2
+ style-to-object: 0.4.4
+ unist-util-position: 4.0.4
+ zwitch: 2.0.4
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
+ /hast-util-to-parse5@8.0.0:
+ resolution: {integrity: sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==}
+ dependencies:
+ '@types/hast': 3.0.4
+ comma-separated-tokens: 2.0.3
+ devlop: 1.1.0
+ property-information: 6.4.1
+ space-separated-tokens: 2.0.2
+ web-namespaces: 2.0.1
+ zwitch: 2.0.4
+ dev: false
+
+ /hast-util-to-text@4.0.0:
+ resolution: {integrity: sha512-EWiE1FSArNBPUo1cKWtzqgnuRQwEeQbQtnFJRYV1hb1BWDgrAlBU0ExptvZMM/KSA82cDpm2sFGf3Dmc5Mza3w==}
+ dependencies:
+ '@types/hast': 3.0.4
+ '@types/unist': 3.0.2
+ hast-util-is-element: 3.0.0
+ unist-util-find-after: 5.0.0
+ dev: false
+
+ /hast-util-whitespace@2.0.1:
+ resolution: {integrity: sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==}
+ dev: false
+
+ /hastscript@8.0.0:
+ resolution: {integrity: sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==}
+ dependencies:
+ '@types/hast': 3.0.4
+ comma-separated-tokens: 2.0.3
+ hast-util-parse-selector: 4.0.0
+ property-information: 6.4.1
+ space-separated-tokens: 2.0.2
+ dev: false
+
+ /heap@0.2.7:
+ resolution: {integrity: sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==}
+ dev: false
+
+ /html-to-text@9.0.5:
+ resolution: {integrity: sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg==}
+ engines: {node: '>=14'}
+ dependencies:
+ '@selderee/plugin-htmlparser2': 0.11.0
+ deepmerge: 4.3.1
+ dom-serializer: 2.0.0
+ htmlparser2: 8.0.2
+ selderee: 0.11.0
+ dev: false
+
+ /html-void-elements@3.0.0:
+ resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
+ dev: false
+
+ /htmlparser2@8.0.2:
+ resolution: {integrity: sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==}
+ dependencies:
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+ domutils: 3.1.0
+ entities: 4.5.0
+ dev: false
+
+ /http_ece@1.2.0:
+ resolution: {integrity: sha512-JrF8SSLVmcvc5NducxgyOrKXe3EsyHMgBFgSaIUGmArKe+rwr0uphRkRXvwiom3I+fpIfoItveHrfudL8/rxuA==}
+ engines: {node: '>=16'}
+ dev: false
+
+ /https-proxy-agent@7.0.4:
+ resolution: {integrity: sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==}
+ engines: {node: '>= 14'}
+ dependencies:
+ agent-base: 7.1.0
+ debug: 4.3.4
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
+ /iconv-lite@0.6.3:
+ resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ safer-buffer: 2.1.2
+ dev: false
+
+ /idb@7.1.1:
+ resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==}
+
+ /ieee754@1.2.1:
+ resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
+ dev: false
+
+ /ignore@5.3.0:
+ resolution: {integrity: sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==}
+ engines: {node: '>= 4'}
+
+ /import-fresh@3.3.0:
+ resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
+ engines: {node: '>=6'}
+ dependencies:
+ parent-module: 1.0.1
+ resolve-from: 4.0.0
+ dev: true
+
+ /imurmurhash@0.1.4:
+ resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+ engines: {node: '>=0.8.19'}
+ dev: true
+
+ /inflight@1.0.6:
+ resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
+ dependencies:
+ once: 1.4.0
+ wrappy: 1.0.2
+
+ /inherits@2.0.4:
+ resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+
+ /ini@1.3.8:
+ resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==}
+ dev: false
+
+ /inline-style-parser@0.1.1:
+ resolution: {integrity: sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q==}
+ dev: false
+
+ /input-otp@1.2.3(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-pxYvgnihL9KAdpcShX2+iKctdMRbDs36bIqd8uIsN3e5vv9VjMv2bhO3S5Bl1PjcDPsA/OXZe5R71n8oVtucfQ==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ dependencies:
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /internal-slot@1.0.6:
+ resolution: {integrity: sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ get-intrinsic: 1.2.2
+ hasown: 2.0.0
+ side-channel: 1.0.4
+
+ /internmap@1.0.1:
+ resolution: {integrity: sha512-lDB5YccMydFBtasVtxnZ3MRBHuaoE8GKsppq+EchKL2U4nK/DmEpPHNH8MZe5HkMtpSiTSOZwfN0tzYjO/lJEw==}
+ dev: false
+
+ /internmap@2.0.3:
+ resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
+ engines: {node: '>=12'}
+ dev: false
+
+ /invariant@2.2.4:
+ resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==}
+ dependencies:
+ loose-envify: 1.4.0
+ dev: false
+
+ /is-alphabetical@2.0.1:
+ resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==}
+ dev: false
+
+ /is-alphanumerical@2.0.1:
+ resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==}
+ dependencies:
+ is-alphabetical: 2.0.1
+ is-decimal: 2.0.1
+ dev: false
+
+ /is-array-buffer@3.0.2:
+ resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==}
+ dependencies:
+ call-bind: 1.0.5
+ get-intrinsic: 1.2.2
+ is-typed-array: 1.1.12
+
+ /is-arrayish@0.3.2:
+ resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==}
+ dev: false
+
+ /is-async-function@2.0.0:
+ resolution: {integrity: sha512-Y1JXKrfykRJGdlDwdKlLpLyMIiWqWvuSd17TvZk68PLAOGOoF4Xyav1z0Xhoi+gCYjZVeC5SI+hYFOfvXmGRCA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ has-tostringtag: 1.0.0
+ dev: true
+
+ /is-bigint@1.0.4:
+ resolution: {integrity: sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==}
+ dependencies:
+ has-bigints: 1.0.2
+
+ /is-binary-path@2.1.0:
+ resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+ engines: {node: '>=8'}
+ dependencies:
+ binary-extensions: 2.2.0
+
+ /is-boolean-object@1.1.2:
+ resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.5
+ has-tostringtag: 1.0.0
+
+ /is-buffer@2.0.5:
+ resolution: {integrity: sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==}
+ engines: {node: '>=4'}
+ dev: false
+
+ /is-callable@1.2.7:
+ resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
+ engines: {node: '>= 0.4'}
+
+ /is-core-module@2.13.1:
+ resolution: {integrity: sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==}
+ dependencies:
+ hasown: 2.0.0
+
+ /is-date-object@1.0.5:
+ resolution: {integrity: sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ has-tostringtag: 1.0.0
+
+ /is-decimal@2.0.1:
+ resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==}
+ dev: false
+
+ /is-extendable@0.1.1:
+ resolution: {integrity: sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==}
+ engines: {node: '>=0.10.0'}
+ dev: false
+
+ /is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+
+ /is-finalizationregistry@1.0.2:
+ resolution: {integrity: sha512-0by5vtUJs8iFQb5TYUHHPudOR+qXYIMKtiUzvLIZITZUjknFmziyBJuLhVRc+Ds0dREFlskDNJKYIdIzu/9pfw==}
+ dependencies:
+ call-bind: 1.0.5
+ dev: true
+
+ /is-fullwidth-code-point@3.0.0:
+ resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+ engines: {node: '>=8'}
+
+ /is-generator-function@1.0.10:
+ resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ has-tostringtag: 1.0.0
+ dev: true
+
+ /is-glob@4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ is-extglob: 2.1.1
+
+ /is-hexadecimal@2.0.1:
+ resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==}
+ dev: false
+
+ /is-map@2.0.2:
+ resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==}
+ dev: true
+
+ /is-module@1.0.0:
+ resolution: {integrity: sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g==}
+
+ /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@7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+
+ /is-obj@1.0.1:
+ resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==}
+ engines: {node: '>=0.10.0'}
+
+ /is-obj@3.0.0:
+ resolution: {integrity: sha512-IlsXEHOjtKhpN8r/tRFj2nDyTmHvcfNeu/nrRIcXE17ROeatXchkojffa1SpdqW4cr/Fj6QkEf/Gn4zf6KKvEQ==}
+ engines: {node: '>=12'}
+ dev: false
+
+ /is-path-cwd@2.2.0:
+ resolution: {integrity: sha512-w942bTcih8fdJPJmQHFzkS76NEP8Kzzvmw92cXsazb8intwLqPibPPdXf4ANdKV3rYMuuQYGIWtvz9JilB3NFQ==}
+ engines: {node: '>=6'}
+ dev: false
+
+ /is-path-in-cwd@2.1.0:
+ resolution: {integrity: sha512-rNocXHgipO+rvnP6dk3zI20RpOtrAM/kzbB258Uw5BWr3TpXi861yzjo16Dn4hUox07iw5AyeMLHWsujkjzvRQ==}
+ engines: {node: '>=6'}
+ dependencies:
+ is-path-inside: 2.1.0
+ dev: false
+
+ /is-path-inside@2.1.0:
+ resolution: {integrity: sha512-wiyhTzfDWsvwAW53OBWF5zuvaOGlZ6PwYxAbPVDhpm+gM09xKQGjBq/8uYN12aDvMxnAnq3dxTyoSoRNmg5YFg==}
+ engines: {node: '>=6'}
+ dependencies:
+ path-is-inside: 1.0.2
+ dev: false
+
+ /is-path-inside@3.0.3:
+ resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /is-plain-obj@3.0.0:
+ resolution: {integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==}
+ engines: {node: '>=10'}
+ dev: false
+
+ /is-plain-obj@4.1.0:
+ resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==}
+ engines: {node: '>=12'}
+ dev: false
+
+ /is-reference@3.0.2:
+ resolution: {integrity: sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==}
+ dependencies:
+ '@types/estree': 1.0.5
+ dev: false
+
+ /is-regex@1.1.4:
+ resolution: {integrity: sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.5
+ has-tostringtag: 1.0.0
+
+ /is-regexp@1.0.0:
+ resolution: {integrity: sha512-7zjFAPO4/gwyQAAgRRmqeEeyIICSdmCqa3tsVHMdBzaXXRiqopZL4Cyghg/XulGWrtABTpbnYYzzIRffLkP4oA==}
+ engines: {node: '>=0.10.0'}
+
+ /is-set@2.0.2:
+ resolution: {integrity: sha512-+2cnTEZeY5z/iXGbLhPrOAaK/Mau5k5eXq9j14CpRTftq0pAJu2MwVRSZhyZWBzx3o6X795Lz6Bpb6R0GKf37g==}
+ dev: true
+
+ /is-shared-array-buffer@1.0.2:
+ resolution: {integrity: sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==}
+ dependencies:
+ call-bind: 1.0.5
+
+ /is-stream@1.1.0:
+ resolution: {integrity: sha512-uQPm8kcs47jx38atAcWTVxyltQYoPT68y9aWYdV6yWXSyW8mzSat0TL6CiWdZeCdF3KrAvpVtnHbTv4RN+rqdQ==}
+ engines: {node: '>=0.10.0'}
+ dev: false
+
+ /is-stream@2.0.1:
+ resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
+ engines: {node: '>=8'}
+
+ /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'}
+ dependencies:
+ has-symbols: 1.0.3
+
+ /is-typed-array@1.1.12:
+ resolution: {integrity: sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ which-typed-array: 1.1.13
+
+ /is-weakmap@2.0.1:
+ resolution: {integrity: sha512-NSBR4kH5oVj1Uwvv970ruUkCV7O1mzgVFO4/rev2cLRda9Tm9HrL70ZPut4rOHgY0FNrUu9BCbXA2sdQ+x0chA==}
+ dev: true
+
+ /is-weakref@1.0.2:
+ resolution: {integrity: sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==}
+ dependencies:
+ call-bind: 1.0.5
+
+ /is-weakset@2.0.2:
+ resolution: {integrity: sha512-t2yVvttHkQktwnNNmBQ98AhENLdPUTDTE21uPqAQ0ARwQfGeQKRVS0NNurH7bTf7RrvcVn1OOge45CnBeHCSmg==}
+ dependencies:
+ call-bind: 1.0.5
+ get-intrinsic: 1.2.2
+ dev: true
+
+ /is-what@4.1.16:
+ resolution: {integrity: sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==}
+ engines: {node: '>=12.13'}
+ dev: false
+
+ /isarray@2.0.5:
+ resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
+
+ /isexe@2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+ /iterator.prototype@1.1.2:
+ resolution: {integrity: sha512-DR33HMMr8EzwuRL8Y9D3u2BMj8+RqSE850jfGu59kS7tbmPLzGkZmVSfyCFSDxuZiEY6Rzt3T2NA/qU+NwVj1w==}
+ dependencies:
+ define-properties: 1.2.1
+ get-intrinsic: 1.2.2
+ has-symbols: 1.0.3
+ reflect.getprototypeof: 1.0.4
+ set-function-name: 2.0.1
+ dev: true
+
+ /jackspeak@2.3.6:
+ resolution: {integrity: sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==}
+ engines: {node: '>=14'}
+ dependencies:
+ '@isaacs/cliui': 8.0.2
+ optionalDependencies:
+ '@pkgjs/parseargs': 0.11.0
+
+ /jake@10.8.7:
+ resolution: {integrity: sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==}
+ engines: {node: '>=10'}
+ hasBin: true
+ dependencies:
+ async: 3.2.5
+ chalk: 4.1.2
+ filelist: 1.0.4
+ minimatch: 3.1.2
+
+ /jest-worker@26.6.2:
+ resolution: {integrity: sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==}
+ engines: {node: '>= 10.13.0'}
+ dependencies:
+ '@types/node': 18.19.8
+ merge-stream: 2.0.0
+ supports-color: 7.2.0
+
+ /jest-worker@27.5.1:
+ resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==}
+ engines: {node: '>= 10.13.0'}
+ dependencies:
+ '@types/node': 18.19.8
+ merge-stream: 2.0.0
+ supports-color: 8.1.1
+ dev: false
+
+ /jiti@1.21.6:
+ resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==}
+ hasBin: true
+
+ /jose@4.15.4:
+ resolution: {integrity: sha512-W+oqK4H+r5sITxfxpSU+MMdr/YSWGvgZMQDIsNoBDGGy4i7GBPTtvFKibQzW06n3U3TqHjhvBJsirShsEJ6eeQ==}
+ dev: false
+
+ /js-beautify@1.15.1:
+ resolution: {integrity: sha512-ESjNzSlt/sWE8sciZH8kBF8BPlwXPwhR6pWKAw8bw4Bwj+iZcnKW6ONWUutJ7eObuBZQpiIb8S7OYspWrKt7rA==}
+ engines: {node: '>=14'}
+ hasBin: true
+ dependencies:
+ config-chain: 1.1.13
+ editorconfig: 1.0.4
+ glob: 10.3.10
+ js-cookie: 3.0.5
+ nopt: 7.2.0
+ dev: false
+
+ /js-cookie@3.0.5:
+ resolution: {integrity: sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==}
+ engines: {node: '>=14'}
+ dev: false
+
+ /js-tokens@4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+ /js-yaml@3.14.1:
+ resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
+ hasBin: true
+ dependencies:
+ argparse: 1.0.10
+ esprima: 4.0.1
+ dev: false
+
+ /js-yaml@4.1.0:
+ resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
+ hasBin: true
+ dependencies:
+ argparse: 2.0.1
+
+ /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
+
+ /json-buffer@3.0.1:
+ resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+ dev: true
+
+ /json-parse-even-better-errors@2.3.1:
+ resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
+ dev: false
+
+ /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-schema@0.4.0:
+ resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
+
+ /json-stable-stringify-without-jsonify@1.0.1:
+ resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+ dev: true
+
+ /json5@1.0.2:
+ resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==}
+ hasBin: true
+ dependencies:
+ minimist: 1.2.8
+ dev: true
+
+ /json5@2.2.3:
+ resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ /jsonc-parser@3.2.1:
+ resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==}
+ dev: false
+
+ /jsonfile@6.1.0:
+ resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
+ dependencies:
+ universalify: 2.0.1
+ optionalDependencies:
+ graceful-fs: 4.2.11
+
+ /jsonpointer@5.0.1:
+ resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==}
+ engines: {node: '>=0.10.0'}
+
+ /jsx-ast-utils@3.3.5:
+ resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==}
+ engines: {node: '>=4.0'}
+ dependencies:
+ array-includes: 3.1.7
+ array.prototype.flat: 1.3.2
+ object.assign: 4.1.5
+ object.values: 1.1.7
+ dev: true
+
+ /jwa@2.0.0:
+ resolution: {integrity: sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==}
+ dependencies:
+ buffer-equal-constant-time: 1.0.1
+ ecdsa-sig-formatter: 1.0.11
+ safe-buffer: 5.2.1
+ dev: false
+
+ /jws@4.0.0:
+ resolution: {integrity: sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==}
+ dependencies:
+ jwa: 2.0.0
+ safe-buffer: 5.2.1
+ dev: false
+
+ /katex@0.16.9:
+ resolution: {integrity: sha512-fsSYjWS0EEOwvy81j3vRA8TEAhQhKiqO+FQaKWp0m39qwOzHVBgAUBIXWj1pB+O2W3fIpNa6Y9KSKCVbfPhyAQ==}
+ hasBin: true
+ dependencies:
+ commander: 8.3.0
+ dev: false
+
+ /keyv@4.5.4:
+ resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+ dependencies:
+ json-buffer: 3.0.1
+ dev: true
+
+ /khroma@2.1.0:
+ resolution: {integrity: sha512-Ls993zuzfayK269Svk9hzpeGUKob/sIgZzyHYdjQoAdQetRKpOLj+k/QQQ/6Qi0Yz65mlROrfd+Ev+1+7dz9Kw==}
+ dev: false
+
+ /kind-of@6.0.3:
+ resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
+ engines: {node: '>=0.10.0'}
+ dev: false
+
+ /kleur@4.1.5:
+ resolution: {integrity: sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==}
+ engines: {node: '>=6'}
+ dev: false
+
+ /language-subtag-registry@0.3.22:
+ resolution: {integrity: sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w==}
+ dev: true
+
+ /language-tags@1.0.9:
+ resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==}
+ engines: {node: '>=0.10'}
+ dependencies:
+ language-subtag-registry: 0.3.22
+ dev: true
+
+ /layout-base@1.0.2:
+ resolution: {integrity: sha512-8h2oVEZNktL4BH2JCOI90iD1yXwL6iNW7KcCKT2QZgQJR2vbqDsldCTPRU9NifTCqHZci57XvQQ15YTu+sTYPg==}
+ dev: false
+
+ /leac@0.6.0:
+ resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==}
+ dev: false
+
+ /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'}
+ dependencies:
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+ dev: true
+
+ /lilconfig@2.1.0:
+ resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==}
+ engines: {node: '>=10'}
+
+ /lilconfig@3.0.0:
+ resolution: {integrity: sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==}
+ engines: {node: '>=14'}
+
+ /lines-and-columns@1.2.4:
+ resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+
+ /loader-runner@4.3.0:
+ resolution: {integrity: sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==}
+ engines: {node: '>=6.11.5'}
+ dev: false
+
+ /loader-utils@2.0.4:
+ resolution: {integrity: sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==}
+ engines: {node: '>=8.9.0'}
+ dependencies:
+ big.js: 5.2.2
+ emojis-list: 3.0.0
+ json5: 2.2.3
+ dev: false
+
+ /locate-path@5.0.0:
+ resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
+ engines: {node: '>=8'}
+ dependencies:
+ p-locate: 4.1.0
+ dev: false
+
+ /locate-path@6.0.0:
+ resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+ engines: {node: '>=10'}
+ dependencies:
+ p-locate: 5.0.0
+ dev: true
+
+ /locate-path@7.2.0:
+ resolution: {integrity: sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ dependencies:
+ p-locate: 6.0.0
+ dev: false
+
+ /lodash-es@4.17.21:
+ resolution: {integrity: sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==}
+ dev: false
+
+ /lodash.debounce@4.0.8:
+ resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
+
+ /lodash.get@4.4.2:
+ resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==}
+ dev: false
+
+ /lodash.merge@4.6.2:
+ resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+ dev: true
+
+ /lodash.sortby@4.7.0:
+ resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==}
+
+ /lodash@4.17.21:
+ resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+
+ /longest-streak@3.1.0:
+ resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==}
+ dev: false
+
+ /loose-envify@1.4.0:
+ resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
+ hasBin: true
+ dependencies:
+ js-tokens: 4.0.0
+
+ /lru-cache@10.1.0:
+ resolution: {integrity: sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==}
+ engines: {node: 14 || >=16.14}
+
+ /lru-cache@4.1.5:
+ resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==}
+ dependencies:
+ pseudomap: 1.0.2
+ yallist: 2.1.2
+ dev: false
+
+ /lru-cache@5.1.1:
+ resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+ dependencies:
+ yallist: 3.1.1
+
+ /lru-cache@6.0.0:
+ resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
+ engines: {node: '>=10'}
+ dependencies:
+ yallist: 4.0.0
+
+ /lucide-react@0.312.0(react@18.2.0):
+ resolution: {integrity: sha512-3UZsqyswRXjW4t+nw+InICewSimjPKHuSxiFYqTshv9xkK3tPPntXk/lvXc9pKlXIxm3v9WKyoxcrB6YHhP+dg==}
+ peerDependencies:
+ react: ^16.5.1 || ^17.0.0 || ^18.0.0
+ dependencies:
+ react: 18.2.0
+ dev: false
+
+ /magic-string@0.25.9:
+ resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
+ dependencies:
+ sourcemap-codec: 1.4.8
+
+ /make-dir@3.1.0:
+ resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
+ engines: {node: '>=8'}
+ dependencies:
+ semver: 6.3.1
+ dev: false
+
+ /markdown-extensions@1.1.1:
+ resolution: {integrity: sha512-WWC0ZuMzCyDHYCasEGs4IPvLyTGftYwh6wIEOULOF0HXcqZlhwRzrK0w2VUlxWA98xnvb/jszw4ZSkJ6ADpM6Q==}
+ engines: {node: '>=0.10.0'}
+ dev: false
+
+ /markdown-table@3.0.3:
+ resolution: {integrity: sha512-Z1NL3Tb1M9wH4XESsCDEksWoKTdlUafKc4pt0GRwjUyXaCFZ+dc3g2erqB6zm3szA2IUSi7VnPI+o/9jnxh9hw==}
+ dev: false
+
+ /mdast-util-definitions@5.1.2:
+ resolution: {integrity: sha512-8SVPMuHqlPME/z3gqVwWY4zVXn8lqKv/pAhC57FuJ40ImXyBpmO5ukh98zB2v7Blql2FiHjHv9LVztSIqjY+MA==}
+ dependencies:
+ '@types/mdast': 3.0.15
+ '@types/unist': 2.0.10
+ unist-util-visit: 4.1.2
+ dev: false
+
+ /mdast-util-find-and-replace@2.2.2:
+ resolution: {integrity: sha512-MTtdFRz/eMDHXzeK6W3dO7mXUlF82Gom4y0oOgvHhh/HXZAGvIQDUvQ0SuUx+j2tv44b8xTHOm8K/9OoRFnXKw==}
+ dependencies:
+ '@types/mdast': 3.0.15
+ escape-string-regexp: 5.0.0
+ unist-util-is: 5.2.1
+ unist-util-visit-parents: 5.1.3
+ dev: false
+
+ /mdast-util-from-markdown@1.3.1:
+ resolution: {integrity: sha512-4xTO/M8c82qBcnQc1tgpNtubGUW/Y1tBQ1B0i5CtSoelOLKFYlElIr3bvgREYYO5iRqbMY1YuqZng0GVOI8Qww==}
+ dependencies:
+ '@types/mdast': 3.0.15
+ '@types/unist': 2.0.10
+ decode-named-character-reference: 1.0.2
+ mdast-util-to-string: 3.2.0
+ micromark: 3.2.0
+ micromark-util-decode-numeric-character-reference: 1.1.0
+ micromark-util-decode-string: 1.1.0
+ micromark-util-normalize-identifier: 1.1.0
+ micromark-util-symbol: 1.1.0
+ micromark-util-types: 1.1.0
+ unist-util-stringify-position: 3.0.3
+ uvu: 0.5.6
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
+ /mdast-util-gfm-autolink-literal@1.0.3:
+ resolution: {integrity: sha512-My8KJ57FYEy2W2LyNom4n3E7hKTuQk/0SES0u16tjA9Z3oFkF4RrC/hPAPgjlSpezsOvI8ObcXcElo92wn5IGA==}
+ dependencies:
+ '@types/mdast': 3.0.15
+ ccount: 2.0.1
+ mdast-util-find-and-replace: 2.2.2
+ micromark-util-character: 1.2.0
+ dev: false
+
+ /mdast-util-gfm-footnote@1.0.2:
+ resolution: {integrity: sha512-56D19KOGbE00uKVj3sgIykpwKL179QsVFwx/DCW0u/0+URsryacI4MAdNJl0dh+u2PSsD9FtxPFbHCzJ78qJFQ==}
+ dependencies:
+ '@types/mdast': 3.0.15
+ mdast-util-to-markdown: 1.5.0
+ micromark-util-normalize-identifier: 1.1.0
+ dev: false
+
+ /mdast-util-gfm-strikethrough@1.0.3:
+ resolution: {integrity: sha512-DAPhYzTYrRcXdMjUtUjKvW9z/FNAMTdU0ORyMcbmkwYNbKocDpdk+PX1L1dQgOID/+vVs1uBQ7ElrBQfZ0cuiQ==}
+ dependencies:
+ '@types/mdast': 3.0.15
+ mdast-util-to-markdown: 1.5.0
+ dev: false
+
+ /mdast-util-gfm-table@1.0.7:
+ resolution: {integrity: sha512-jjcpmNnQvrmN5Vx7y7lEc2iIOEytYv7rTvu+MeyAsSHTASGCCRA79Igg2uKssgOs1i1po8s3plW0sTu1wkkLGg==}
+ dependencies:
+ '@types/mdast': 3.0.15
+ markdown-table: 3.0.3
+ mdast-util-from-markdown: 1.3.1
+ mdast-util-to-markdown: 1.5.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
+ /mdast-util-gfm-task-list-item@1.0.2:
+ resolution: {integrity: sha512-PFTA1gzfp1B1UaiJVyhJZA1rm0+Tzn690frc/L8vNX1Jop4STZgOE6bxUhnzdVSB+vm2GU1tIsuQcA9bxTQpMQ==}
+ dependencies:
+ '@types/mdast': 3.0.15
+ mdast-util-to-markdown: 1.5.0
+ dev: false
+
+ /mdast-util-gfm@2.0.2:
+ resolution: {integrity: sha512-qvZ608nBppZ4icQlhQQIAdc6S3Ffj9RGmzwUKUWuEICFnd1LVkN3EktF7ZHAgfcEdvZB5owU9tQgt99e2TlLjg==}
+ dependencies:
+ mdast-util-from-markdown: 1.3.1
+ mdast-util-gfm-autolink-literal: 1.0.3
+ mdast-util-gfm-footnote: 1.0.2
+ mdast-util-gfm-strikethrough: 1.0.3
+ mdast-util-gfm-table: 1.0.7
+ mdast-util-gfm-task-list-item: 1.0.2
+ mdast-util-to-markdown: 1.5.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
+ /mdast-util-math@2.0.2:
+ resolution: {integrity: sha512-8gmkKVp9v6+Tgjtq6SYx9kGPpTf6FVYRa53/DLh479aldR9AyP48qeVOgNZ5X7QUK7nOy4yw7vg6mbiGcs9jWQ==}
+ dependencies:
+ '@types/mdast': 3.0.15
+ longest-streak: 3.1.0
+ mdast-util-to-markdown: 1.5.0
+ dev: false
+
+ /mdast-util-mdx-expression@1.3.2:
+ resolution: {integrity: sha512-xIPmR5ReJDu/DHH1OoIT1HkuybIfRGYRywC+gJtI7qHjCJp/M9jrmBEJW22O8lskDWm562BX2W8TiAwRTb0rKA==}
+ dependencies:
+ '@types/estree-jsx': 1.0.5
+ '@types/hast': 2.3.10
+ '@types/mdast': 3.0.15
+ mdast-util-from-markdown: 1.3.1
+ mdast-util-to-markdown: 1.5.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
+ /mdast-util-mdx-jsx@2.1.4:
+ resolution: {integrity: sha512-DtMn9CmVhVzZx3f+optVDF8yFgQVt7FghCRNdlIaS3X5Bnym3hZwPbg/XW86vdpKjlc1PVj26SpnLGeJBXD3JA==}
+ dependencies:
+ '@types/estree-jsx': 1.0.5
+ '@types/hast': 2.3.10
+ '@types/mdast': 3.0.15
+ '@types/unist': 2.0.10
+ ccount: 2.0.1
+ mdast-util-from-markdown: 1.3.1
+ mdast-util-to-markdown: 1.5.0
+ parse-entities: 4.0.1
+ stringify-entities: 4.0.3
+ unist-util-remove-position: 4.0.2
+ unist-util-stringify-position: 3.0.3
+ vfile-message: 3.1.4
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
+ /mdast-util-mdx@2.0.1:
+ resolution: {integrity: sha512-38w5y+r8nyKlGvNjSEqWrhG0w5PmnRA+wnBvm+ulYCct7nsGYhFVb0lljS9bQav4psDAS1eGkP2LMVcZBi/aqw==}
+ dependencies:
+ mdast-util-from-markdown: 1.3.1
+ mdast-util-mdx-expression: 1.3.2
+ mdast-util-mdx-jsx: 2.1.4
+ mdast-util-mdxjs-esm: 1.3.1
+ mdast-util-to-markdown: 1.5.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
+ /mdast-util-mdxjs-esm@1.3.1:
+ resolution: {integrity: sha512-SXqglS0HrEvSdUEfoXFtcg7DRl7S2cwOXc7jkuusG472Mmjag34DUDeOJUZtl+BVnyeO1frIgVpHlNRWc2gk/w==}
+ dependencies:
+ '@types/estree-jsx': 1.0.5
+ '@types/hast': 2.3.10
+ '@types/mdast': 3.0.15
+ mdast-util-from-markdown: 1.3.1
+ mdast-util-to-markdown: 1.5.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
+ /mdast-util-phrasing@3.0.1:
+ resolution: {integrity: sha512-WmI1gTXUBJo4/ZmSk79Wcb2HcjPJBzM1nlI/OUWA8yk2X9ik3ffNbBGsU+09BFmXaL1IBb9fiuvq6/KMiNycSg==}
+ dependencies:
+ '@types/mdast': 3.0.15
+ unist-util-is: 5.2.1
+ dev: false
+
+ /mdast-util-to-hast@12.3.0:
+ resolution: {integrity: sha512-pits93r8PhnIoU4Vy9bjW39M2jJ6/tdHyja9rrot9uujkN7UTU9SDnE6WNJz/IGyQk3XHX6yNNtrBH6cQzm8Hw==}
+ dependencies:
+ '@types/hast': 2.3.10
+ '@types/mdast': 3.0.15
+ mdast-util-definitions: 5.1.2
+ micromark-util-sanitize-uri: 1.2.0
+ trim-lines: 3.0.1
+ unist-util-generated: 2.0.1
+ unist-util-position: 4.0.4
+ unist-util-visit: 4.1.2
+ dev: false
+
+ /mdast-util-to-hast@13.1.0:
+ resolution: {integrity: sha512-/e2l/6+OdGp/FB+ctrJ9Avz71AN/GRH3oi/3KAx/kMnoUsD6q0woXlDT8lLEeViVKE7oZxE7RXzvO3T8kF2/sA==}
+ dependencies:
+ '@types/hast': 3.0.4
+ '@types/mdast': 4.0.3
+ '@ungap/structured-clone': 1.2.0
+ devlop: 1.1.0
+ micromark-util-sanitize-uri: 2.0.0
+ trim-lines: 3.0.1
+ unist-util-position: 5.0.0
+ unist-util-visit: 5.0.0
+ vfile: 6.0.1
+ dev: false
+
+ /mdast-util-to-markdown@1.5.0:
+ resolution: {integrity: sha512-bbv7TPv/WC49thZPg3jXuqzuvI45IL2EVAr/KxF0BSdHsU0ceFHOmwQn6evxAh1GaoK/6GQ1wp4R4oW2+LFL/A==}
+ dependencies:
+ '@types/mdast': 3.0.15
+ '@types/unist': 2.0.10
+ longest-streak: 3.1.0
+ mdast-util-phrasing: 3.0.1
+ mdast-util-to-string: 3.2.0
+ micromark-util-decode-string: 1.1.0
+ unist-util-visit: 4.1.2
+ zwitch: 2.0.4
+ dev: false
+
+ /mdast-util-to-string@3.2.0:
+ resolution: {integrity: sha512-V4Zn/ncyN1QNSqSBxTrMOLpjr+IKdHl2v3KVLoWmDPscP4r9GcCi71gjgvUV1SFSKh92AjAG4peFuBl2/YgCJg==}
+ dependencies:
+ '@types/mdast': 3.0.15
+ 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'}
+
+ /mermaid@10.9.0:
+ resolution: {integrity: sha512-swZju0hFox/B/qoLKK0rOxxgh8Cf7rJSfAUc1u8fezVihYMvrJAS45GzAxTVf4Q+xn9uMgitBcmWk7nWGXOs/g==}
+ dependencies:
+ '@braintree/sanitize-url': 6.0.4
+ '@types/d3-scale': 4.0.8
+ '@types/d3-scale-chromatic': 3.0.3
+ cytoscape: 3.28.1
+ cytoscape-cose-bilkent: 4.1.0(cytoscape@3.28.1)
+ d3: 7.9.0
+ d3-sankey: 0.12.3
+ dagre-d3-es: 7.0.10
+ dayjs: 1.11.10
+ dompurify: 3.0.9
+ elkjs: 0.9.2
+ katex: 0.16.9
+ khroma: 2.1.0
+ lodash-es: 4.17.21
+ mdast-util-from-markdown: 1.3.1
+ non-layered-tidy-tree-layout: 2.0.2
+ stylis: 4.3.1
+ ts-dedent: 2.2.0
+ uuid: 9.0.1
+ web-worker: 1.3.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
+ /micromark-core-commonmark@1.1.0:
+ resolution: {integrity: sha512-BgHO1aRbolh2hcrzL2d1La37V0Aoz73ymF8rAcKnohLy93titmv62E0gP8Hrx9PKcKrqCZ1BbLGbP3bEhoXYlw==}
+ dependencies:
+ decode-named-character-reference: 1.0.2
+ micromark-factory-destination: 1.1.0
+ micromark-factory-label: 1.1.0
+ micromark-factory-space: 1.1.0
+ micromark-factory-title: 1.1.0
+ micromark-factory-whitespace: 1.1.0
+ micromark-util-character: 1.2.0
+ micromark-util-chunked: 1.1.0
+ micromark-util-classify-character: 1.1.0
+ micromark-util-html-tag-name: 1.2.0
+ micromark-util-normalize-identifier: 1.1.0
+ micromark-util-resolve-all: 1.1.0
+ micromark-util-subtokenize: 1.1.0
+ micromark-util-symbol: 1.1.0
+ micromark-util-types: 1.1.0
+ uvu: 0.5.6
+ dev: false
+
+ /micromark-extension-gfm-autolink-literal@1.0.5:
+ resolution: {integrity: sha512-z3wJSLrDf8kRDOh2qBtoTRD53vJ+CWIyo7uyZuxf/JAbNJjiHsOpG1y5wxk8drtv3ETAHutCu6N3thkOOgueWg==}
+ dependencies:
+ micromark-util-character: 1.2.0
+ micromark-util-sanitize-uri: 1.2.0
+ micromark-util-symbol: 1.1.0
+ micromark-util-types: 1.1.0
+ dev: false
+
+ /micromark-extension-gfm-footnote@1.1.2:
+ resolution: {integrity: sha512-Yxn7z7SxgyGWRNa4wzf8AhYYWNrwl5q1Z8ii+CSTTIqVkmGZF1CElX2JI8g5yGoM3GAman9/PVCUFUSJ0kB/8Q==}
+ dependencies:
+ micromark-core-commonmark: 1.1.0
+ micromark-factory-space: 1.1.0
+ micromark-util-character: 1.2.0
+ micromark-util-normalize-identifier: 1.1.0
+ micromark-util-sanitize-uri: 1.2.0
+ micromark-util-symbol: 1.1.0
+ micromark-util-types: 1.1.0
+ uvu: 0.5.6
+ dev: false
+
+ /micromark-extension-gfm-strikethrough@1.0.7:
+ resolution: {integrity: sha512-sX0FawVE1o3abGk3vRjOH50L5TTLr3b5XMqnP9YDRb34M0v5OoZhG+OHFz1OffZ9dlwgpTBKaT4XW/AsUVnSDw==}
+ dependencies:
+ micromark-util-chunked: 1.1.0
+ micromark-util-classify-character: 1.1.0
+ micromark-util-resolve-all: 1.1.0
+ micromark-util-symbol: 1.1.0
+ micromark-util-types: 1.1.0
+ uvu: 0.5.6
+ dev: false
+
+ /micromark-extension-gfm-table@1.0.7:
+ resolution: {integrity: sha512-3ZORTHtcSnMQEKtAOsBQ9/oHp9096pI/UvdPtN7ehKvrmZZ2+bbWhi0ln+I9drmwXMt5boocn6OlwQzNXeVeqw==}
+ dependencies:
+ micromark-factory-space: 1.1.0
+ micromark-util-character: 1.2.0
+ micromark-util-symbol: 1.1.0
+ micromark-util-types: 1.1.0
+ uvu: 0.5.6
+ dev: false
+
+ /micromark-extension-gfm-tagfilter@1.0.2:
+ resolution: {integrity: sha512-5XWB9GbAUSHTn8VPU8/1DBXMuKYT5uOgEjJb8gN3mW0PNW5OPHpSdojoqf+iq1xo7vWzw/P8bAHY0n6ijpXF7g==}
+ dependencies:
+ micromark-util-types: 1.1.0
+ dev: false
+
+ /micromark-extension-gfm-task-list-item@1.0.5:
+ resolution: {integrity: sha512-RMFXl2uQ0pNQy6Lun2YBYT9g9INXtWJULgbt01D/x8/6yJ2qpKyzdZD3pi6UIkzF++Da49xAelVKUeUMqd5eIQ==}
+ dependencies:
+ micromark-factory-space: 1.1.0
+ micromark-util-character: 1.2.0
+ micromark-util-symbol: 1.1.0
+ micromark-util-types: 1.1.0
+ uvu: 0.5.6
+ dev: false
+
+ /micromark-extension-gfm@2.0.3:
+ resolution: {integrity: sha512-vb9OoHqrhCmbRidQv/2+Bc6pkP0FrtlhurxZofvOEy5o8RtuuvTq+RQ1Vw5ZDNrVraQZu3HixESqbG+0iKk/MQ==}
+ dependencies:
+ micromark-extension-gfm-autolink-literal: 1.0.5
+ micromark-extension-gfm-footnote: 1.1.2
+ micromark-extension-gfm-strikethrough: 1.0.7
+ micromark-extension-gfm-table: 1.0.7
+ micromark-extension-gfm-tagfilter: 1.0.2
+ micromark-extension-gfm-task-list-item: 1.0.5
+ micromark-util-combine-extensions: 1.1.0
+ micromark-util-types: 1.1.0
+ dev: false
+
+ /micromark-extension-math@2.1.2:
+ resolution: {integrity: sha512-es0CcOV89VNS9wFmyn+wyFTKweXGW4CEvdaAca6SWRWPyYCbBisnjaHLjWO4Nszuiud84jCpkHsqAJoa768Pvg==}
+ dependencies:
+ '@types/katex': 0.16.7
+ katex: 0.16.9
+ micromark-factory-space: 1.1.0
+ micromark-util-character: 1.2.0
+ micromark-util-symbol: 1.1.0
+ micromark-util-types: 1.1.0
+ uvu: 0.5.6
+ dev: false
+
+ /micromark-extension-mdx-expression@1.0.8:
+ resolution: {integrity: sha512-zZpeQtc5wfWKdzDsHRBY003H2Smg+PUi2REhqgIhdzAa5xonhP03FcXxqFSerFiNUr5AWmHpaNPQTBVOS4lrXw==}
+ dependencies:
+ '@types/estree': 1.0.5
+ micromark-factory-mdx-expression: 1.0.9
+ micromark-factory-space: 1.1.0
+ micromark-util-character: 1.2.0
+ micromark-util-events-to-acorn: 1.2.3
+ micromark-util-symbol: 1.1.0
+ micromark-util-types: 1.1.0
+ uvu: 0.5.6
+ dev: false
+
+ /micromark-extension-mdx-jsx@1.0.5:
+ resolution: {integrity: sha512-gPH+9ZdmDflbu19Xkb8+gheqEDqkSpdCEubQyxuz/Hn8DOXiXvrXeikOoBA71+e8Pfi0/UYmU3wW3H58kr7akA==}
+ dependencies:
+ '@types/acorn': 4.0.6
+ '@types/estree': 1.0.5
+ estree-util-is-identifier-name: 2.1.0
+ micromark-factory-mdx-expression: 1.0.9
+ micromark-factory-space: 1.1.0
+ micromark-util-character: 1.2.0
+ micromark-util-symbol: 1.1.0
+ micromark-util-types: 1.1.0
+ uvu: 0.5.6
+ vfile-message: 3.1.4
+ dev: false
+
+ /micromark-extension-mdx-md@1.0.1:
+ resolution: {integrity: sha512-7MSuj2S7xjOQXAjjkbjBsHkMtb+mDGVW6uI2dBL9snOBCbZmoNgDAeZ0nSn9j3T42UE/g2xVNMn18PJxZvkBEA==}
+ dependencies:
+ micromark-util-types: 1.1.0
+ dev: false
+
+ /micromark-extension-mdxjs-esm@1.0.5:
+ resolution: {integrity: sha512-xNRBw4aoURcyz/S69B19WnZAkWJMxHMT5hE36GtDAyhoyn/8TuAeqjFJQlwk+MKQsUD7b3l7kFX+vlfVWgcX1w==}
+ dependencies:
+ '@types/estree': 1.0.5
+ micromark-core-commonmark: 1.1.0
+ micromark-util-character: 1.2.0
+ micromark-util-events-to-acorn: 1.2.3
+ micromark-util-symbol: 1.1.0
+ micromark-util-types: 1.1.0
+ unist-util-position-from-estree: 1.1.2
+ uvu: 0.5.6
+ vfile-message: 3.1.4
+ dev: false
+
+ /micromark-extension-mdxjs@1.0.1:
+ resolution: {integrity: sha512-7YA7hF6i5eKOfFUzZ+0z6avRG52GpWR8DL+kN47y3f2KhxbBZMhmxe7auOeaTBrW2DenbbZTf1ea9tA2hDpC2Q==}
+ dependencies:
+ acorn: 8.11.3
+ acorn-jsx: 5.3.2(acorn@8.11.3)
+ micromark-extension-mdx-expression: 1.0.8
+ micromark-extension-mdx-jsx: 1.0.5
+ micromark-extension-mdx-md: 1.0.1
+ micromark-extension-mdxjs-esm: 1.0.5
+ micromark-util-combine-extensions: 1.1.0
+ micromark-util-types: 1.1.0
+ dev: false
+
+ /micromark-factory-destination@1.1.0:
+ resolution: {integrity: sha512-XaNDROBgx9SgSChd69pjiGKbV+nfHGDPVYFs5dOoDd7ZnMAE+Cuu91BCpsY8RT2NP9vo/B8pds2VQNCLiu0zhg==}
+ dependencies:
+ micromark-util-character: 1.2.0
+ micromark-util-symbol: 1.1.0
+ micromark-util-types: 1.1.0
+ dev: false
+
+ /micromark-factory-label@1.1.0:
+ resolution: {integrity: sha512-OLtyez4vZo/1NjxGhcpDSbHQ+m0IIGnT8BoPamh+7jVlzLJBH98zzuCoUeMxvM6WsNeh8wx8cKvqLiPHEACn0w==}
+ dependencies:
+ micromark-util-character: 1.2.0
+ micromark-util-symbol: 1.1.0
+ micromark-util-types: 1.1.0
+ uvu: 0.5.6
+ dev: false
+
+ /micromark-factory-mdx-expression@1.0.9:
+ resolution: {integrity: sha512-jGIWzSmNfdnkJq05c7b0+Wv0Kfz3NJ3N4cBjnbO4zjXIlxJr+f8lk+5ZmwFvqdAbUy2q6B5rCY//g0QAAaXDWA==}
+ dependencies:
+ '@types/estree': 1.0.5
+ micromark-util-character: 1.2.0
+ micromark-util-events-to-acorn: 1.2.3
+ micromark-util-symbol: 1.1.0
+ micromark-util-types: 1.1.0
+ unist-util-position-from-estree: 1.1.2
+ uvu: 0.5.6
+ vfile-message: 3.1.4
+ dev: false
+
+ /micromark-factory-space@1.1.0:
+ resolution: {integrity: sha512-cRzEj7c0OL4Mw2v6nwzttyOZe8XY/Z8G0rzmWQZTBi/jjwyw/U4uqKtUORXQrR5bAZZnbTI/feRV/R7hc4jQYQ==}
+ dependencies:
+ micromark-util-character: 1.2.0
+ micromark-util-types: 1.1.0
+ dev: false
+
+ /micromark-factory-title@1.1.0:
+ resolution: {integrity: sha512-J7n9R3vMmgjDOCY8NPw55jiyaQnH5kBdV2/UXCtZIpnHH3P6nHUKaH7XXEYuWwx/xUJcawa8plLBEjMPU24HzQ==}
+ dependencies:
+ micromark-factory-space: 1.1.0
+ micromark-util-character: 1.2.0
+ micromark-util-symbol: 1.1.0
+ micromark-util-types: 1.1.0
+ dev: false
+
+ /micromark-factory-whitespace@1.1.0:
+ resolution: {integrity: sha512-v2WlmiymVSp5oMg+1Q0N1Lxmt6pMhIHD457whWM7/GUlEks1hI9xj5w3zbc4uuMKXGisksZk8DzP2UyGbGqNsQ==}
+ dependencies:
+ micromark-factory-space: 1.1.0
+ micromark-util-character: 1.2.0
+ micromark-util-symbol: 1.1.0
+ micromark-util-types: 1.1.0
+ dev: false
+
+ /micromark-util-character@1.2.0:
+ resolution: {integrity: sha512-lXraTwcX3yH/vMDaFWCQJP1uIszLVebzUa3ZHdrgxr7KEU/9mL4mVgCpGbyhvNLNlauROiNUq7WN5u7ndbY6xg==}
+ dependencies:
+ micromark-util-symbol: 1.1.0
+ micromark-util-types: 1.1.0
+ dev: false
+
+ /micromark-util-character@2.1.0:
+ resolution: {integrity: sha512-KvOVV+X1yLBfs9dCBSopq/+G1PcgT3lAK07mC4BzXi5E7ahzMAF8oIupDDJ6mievI6F+lAATkbQQlQixJfT3aQ==}
+ dependencies:
+ micromark-util-symbol: 2.0.0
+ micromark-util-types: 2.0.0
+ dev: false
+
+ /micromark-util-chunked@1.1.0:
+ resolution: {integrity: sha512-Ye01HXpkZPNcV6FiyoW2fGZDUw4Yc7vT0E9Sad83+bEDiCJ1uXu0S3mr8WLpsz3HaG3x2q0HM6CTuPdcZcluFQ==}
+ dependencies:
+ micromark-util-symbol: 1.1.0
+ dev: false
+
+ /micromark-util-classify-character@1.1.0:
+ resolution: {integrity: sha512-SL0wLxtKSnklKSUplok1WQFoGhUdWYKggKUiqhX+Swala+BtptGCu5iPRc+xvzJ4PXE/hwM3FNXsfEVgoZsWbw==}
+ dependencies:
+ micromark-util-character: 1.2.0
+ micromark-util-symbol: 1.1.0
+ micromark-util-types: 1.1.0
+ dev: false
+
+ /micromark-util-combine-extensions@1.1.0:
+ resolution: {integrity: sha512-Q20sp4mfNf9yEqDL50WwuWZHUrCO4fEyeDCnMGmG5Pr0Cz15Uo7KBs6jq+dq0EgX4DPwwrh9m0X+zPV1ypFvUA==}
+ dependencies:
+ micromark-util-chunked: 1.1.0
+ micromark-util-types: 1.1.0
+ dev: false
+
+ /micromark-util-decode-numeric-character-reference@1.1.0:
+ resolution: {integrity: sha512-m9V0ExGv0jB1OT21mrWcuf4QhP46pH1KkfWy9ZEezqHKAxkj4mPCy3nIH1rkbdMlChLHX531eOrymlwyZIf2iw==}
+ dependencies:
+ micromark-util-symbol: 1.1.0
+ dev: false
+
+ /micromark-util-decode-string@1.1.0:
+ resolution: {integrity: sha512-YphLGCK8gM1tG1bd54azwyrQRjCFcmgj2S2GoJDNnh4vYtnL38JS8M4gpxzOPNyHdNEpheyWXCTnnTDY3N+NVQ==}
+ dependencies:
+ decode-named-character-reference: 1.0.2
+ micromark-util-character: 1.2.0
+ micromark-util-decode-numeric-character-reference: 1.1.0
+ micromark-util-symbol: 1.1.0
+ dev: false
+
+ /micromark-util-encode@1.1.0:
+ resolution: {integrity: sha512-EuEzTWSTAj9PA5GOAs992GzNh2dGQO52UvAbtSOMvXTxv3Criqb6IOzJUBCmEqrrXSblJIJBbFFv6zPxpreiJw==}
+ dev: false
+
+ /micromark-util-encode@2.0.0:
+ resolution: {integrity: sha512-pS+ROfCXAGLWCOc8egcBvT0kf27GoWMqtdarNfDcjb6YLuV5cM3ioG45Ys2qOVqeqSbjaKg72vU+Wby3eddPsA==}
+ dev: false
+
+ /micromark-util-events-to-acorn@1.2.3:
+ resolution: {integrity: sha512-ij4X7Wuc4fED6UoLWkmo0xJQhsktfNh1J0m8g4PbIMPlx+ek/4YdW5mvbye8z/aZvAPUoxgXHrwVlXAPKMRp1w==}
+ dependencies:
+ '@types/acorn': 4.0.6
+ '@types/estree': 1.0.5
+ '@types/unist': 2.0.10
+ estree-util-visit: 1.2.1
+ micromark-util-symbol: 1.1.0
+ micromark-util-types: 1.1.0
+ uvu: 0.5.6
+ vfile-message: 3.1.4
+ dev: false
+
+ /micromark-util-html-tag-name@1.2.0:
+ resolution: {integrity: sha512-VTQzcuQgFUD7yYztuQFKXT49KghjtETQ+Wv/zUjGSGBioZnkA4P1XXZPT1FHeJA6RwRXSF47yvJ1tsJdoxwO+Q==}
+ dev: false
+
+ /micromark-util-normalize-identifier@1.1.0:
+ resolution: {integrity: sha512-N+w5vhqrBihhjdpM8+5Xsxy71QWqGn7HYNUvch71iV2PM7+E3uWGox1Qp90loa1ephtCxG2ftRV/Conitc6P2Q==}
+ dependencies:
+ micromark-util-symbol: 1.1.0
+ dev: false
+
+ /micromark-util-resolve-all@1.1.0:
+ resolution: {integrity: sha512-b/G6BTMSg+bX+xVCshPTPyAu2tmA0E4X98NSR7eIbeC6ycCqCeE7wjfDIgzEbkzdEVJXRtOG4FbEm/uGbCRouA==}
+ dependencies:
+ micromark-util-types: 1.1.0
+ dev: false
+
+ /micromark-util-sanitize-uri@1.2.0:
+ resolution: {integrity: sha512-QO4GXv0XZfWey4pYFndLUKEAktKkG5kZTdUNaTAkzbuJxn2tNBOr+QtxR2XpWaMhbImT2dPzyLrPXLlPhph34A==}
+ dependencies:
+ micromark-util-character: 1.2.0
+ micromark-util-encode: 1.1.0
+ micromark-util-symbol: 1.1.0
+ dev: false
+
+ /micromark-util-sanitize-uri@2.0.0:
+ resolution: {integrity: sha512-WhYv5UEcZrbAtlsnPuChHUAsu/iBPOVaEVsntLBIdpibO0ddy8OzavZz3iL2xVvBZOpolujSliP65Kq0/7KIYw==}
+ dependencies:
+ micromark-util-character: 2.1.0
+ micromark-util-encode: 2.0.0
+ micromark-util-symbol: 2.0.0
+ dev: false
+
+ /micromark-util-subtokenize@1.1.0:
+ resolution: {integrity: sha512-kUQHyzRoxvZO2PuLzMt2P/dwVsTiivCK8icYTeR+3WgbuPqfHgPPy7nFKbeqRivBvn/3N3GBiNC+JRTMSxEC7A==}
+ dependencies:
+ micromark-util-chunked: 1.1.0
+ micromark-util-symbol: 1.1.0
+ micromark-util-types: 1.1.0
+ uvu: 0.5.6
+ dev: false
+
+ /micromark-util-symbol@1.1.0:
+ resolution: {integrity: sha512-uEjpEYY6KMs1g7QfJ2eX1SQEV+ZT4rUD3UcF6l57acZvLNK7PBZL+ty82Z1qhK1/yXIY4bdx04FKMgR0g4IAag==}
+ dev: false
+
+ /micromark-util-symbol@2.0.0:
+ resolution: {integrity: sha512-8JZt9ElZ5kyTnO94muPxIGS8oyElRJaiJO8EzV6ZSyGQ1Is8xwl4Q45qU5UOg+bGH4AikWziz0iN4sFLWs8PGw==}
+ dev: false
+
+ /micromark-util-types@1.1.0:
+ resolution: {integrity: sha512-ukRBgie8TIAcacscVHSiddHjO4k/q3pnedmzMQ4iwDcK0FtFCohKOlFbaOL/mPgfnPsL3C1ZyxJa4sbWrBl3jg==}
+ dev: false
+
+ /micromark-util-types@2.0.0:
+ resolution: {integrity: sha512-oNh6S2WMHWRZrmutsRmDDfkzKtxF+bc2VxLC9dvtrDIRFln627VsFP6fLMgTryGDljgLPjkrzQSDcPrjPyDJ5w==}
+ dev: false
+
+ /micromark@3.2.0:
+ resolution: {integrity: sha512-uD66tJj54JLYq0De10AhWycZWGQNUvDI55xPgk2sQM5kn1JYlhbCMTtEeT27+vAhW2FBQxLlOmS3pmA7/2z4aA==}
+ dependencies:
+ '@types/debug': 4.1.12
+ debug: 4.3.4
+ decode-named-character-reference: 1.0.2
+ micromark-core-commonmark: 1.1.0
+ micromark-factory-space: 1.1.0
+ micromark-util-character: 1.2.0
+ micromark-util-chunked: 1.1.0
+ micromark-util-combine-extensions: 1.1.0
+ micromark-util-decode-numeric-character-reference: 1.1.0
+ micromark-util-encode: 1.1.0
+ micromark-util-normalize-identifier: 1.1.0
+ micromark-util-resolve-all: 1.1.0
+ micromark-util-sanitize-uri: 1.2.0
+ micromark-util-subtokenize: 1.1.0
+ micromark-util-symbol: 1.1.0
+ micromark-util-types: 1.1.0
+ uvu: 0.5.6
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
+ /micromatch@4.0.5:
+ resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
+ engines: {node: '>=8.6'}
+ dependencies:
+ braces: 3.0.2
+ picomatch: 2.3.1
+
+ /mime-db@1.52.0:
+ resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+ engines: {node: '>= 0.6'}
+ dev: false
+
+ /mime-types@2.1.35:
+ resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+ engines: {node: '>= 0.6'}
+ dependencies:
+ mime-db: 1.52.0
+ dev: false
+
+ /mimic-response@3.1.0:
+ resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==}
+ engines: {node: '>=10'}
+ dev: false
+
+ /minimalistic-assert@1.0.1:
+ resolution: {integrity: sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==}
+ dev: false
+
+ /minimatch@3.1.2:
+ resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+ dependencies:
+ brace-expansion: 1.1.11
+
+ /minimatch@5.1.6:
+ resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
+ engines: {node: '>=10'}
+ 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'}
+ dependencies:
+ brace-expansion: 2.0.1
+ dev: false
+
+ /minimatch@9.0.3:
+ resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==}
+ engines: {node: '>=16 || 14 >=14.17'}
+ dependencies:
+ brace-expansion: 2.0.1
+
+ /minimist@1.2.8:
+ resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+
+ /minipass@7.0.4:
+ resolution: {integrity: sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ /mkdirp-classic@0.5.3:
+ resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==}
+ dev: false
+
+ /mri@1.2.0:
+ resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
+ engines: {node: '>=4'}
+ dev: false
+
+ /ms@2.1.2:
+ resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
+
+ /ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+ dev: true
+
+ /mz@2.7.0:
+ resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
+ dependencies:
+ any-promise: 1.3.0
+ object-assign: 4.1.1
+ thenify-all: 1.6.0
+
+ /nanoid@3.3.7:
+ resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ /nanoid@5.0.6:
+ resolution: {integrity: sha512-rRq0eMHoGZxlvaFOUdK1Ev83Bd1IgzzR+WJ3IbDJ7QOSdAxYjlurSPqFs9s4lJg29RT6nPwizFtJhQS6V5xgiA==}
+ engines: {node: ^18 || >=20}
+ hasBin: true
+ dev: false
+
+ /napi-build-utils@1.0.2:
+ resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==}
+ dev: false
+
+ /natural-compare@1.4.0:
+ resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+ dev: true
+
+ /neo-async@2.6.2:
+ resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==}
+ dev: false
+
+ /next-auth@4.24.5(next@14.1.0)(nodemailer@6.9.8)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-3RafV3XbfIKk6rF6GlLE4/KxjTcuMCifqrmD+98ejFq73SRoj2rmzoca8u764977lH/Q7jo6Xu6yM+Re1Mz/Og==}
+ peerDependencies:
+ next: ^12.2.5 || ^13 || ^14
+ nodemailer: ^6.6.5
+ react: ^17.0.2 || ^18
+ react-dom: ^17.0.2 || ^18
+ peerDependenciesMeta:
+ nodemailer:
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.23.8
+ '@panva/hkdf': 1.1.1
+ cookie: 0.5.0
+ jose: 4.15.4
+ next: 14.1.0(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0)
+ nodemailer: 6.9.8
+ oauth: 0.9.15
+ openid-client: 5.6.4
+ preact: 10.19.3
+ preact-render-to-string: 5.2.6(preact@10.19.3)
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ uuid: 8.3.2
+ dev: false
+
+ /next-mdx-remote@4.4.1(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-1BvyXaIou6xy3XoNF4yaMZUCb6vD2GTAa5ciOa6WoO+gAUTYsb1K4rI/HSC2ogAWLrb/7VSV52skz07vOzmqIQ==}
+ engines: {node: '>=14', npm: '>=7'}
+ peerDependencies:
+ react: '>=16.x <=18.x'
+ react-dom: '>=16.x <=18.x'
+ dependencies:
+ '@mdx-js/mdx': 2.3.0
+ '@mdx-js/react': 2.3.0(react@18.2.0)
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ vfile: 5.3.7
+ vfile-matter: 3.0.1
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
+ /next-pwa@5.6.0(@babel/core@7.23.9)(next@14.1.0)(webpack@5.90.1):
+ resolution: {integrity: sha512-XV8g8C6B7UmViXU8askMEYhWwQ4qc/XqJGnexbLV68hzKaGHZDMtHsm2TNxFcbR7+ypVuth/wwpiIlMwpRJJ5A==}
+ peerDependencies:
+ next: '>=9.0.0'
+ dependencies:
+ babel-loader: 8.3.0(@babel/core@7.23.9)(webpack@5.90.1)
+ clean-webpack-plugin: 4.0.0(webpack@5.90.1)
+ globby: 11.1.0
+ next: 14.1.0(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0)
+ terser-webpack-plugin: 5.3.10(webpack@5.90.1)
+ workbox-webpack-plugin: 6.6.0(webpack@5.90.1)
+ workbox-window: 6.6.0
+ transitivePeerDependencies:
+ - '@babel/core'
+ - '@swc/core'
+ - '@types/babel__core'
+ - esbuild
+ - supports-color
+ - uglify-js
+ - webpack
+ dev: false
+
+ /next-themes@0.2.1(next@14.1.0)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-B+AKNfYNIzh0vqQQKqQItTS8evEouKD7H5Hj3kmuPERwddR2TxvDSFZuTj6T7Jfn1oyeUyJMydPl1Bkxkh0W7A==}
+ peerDependencies:
+ next: '*'
+ react: '*'
+ react-dom: '*'
+ dependencies:
+ next: 14.1.0(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0)
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /next@13.5.6(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-Y2wTcTbO4WwEsVb4A8VSnOsG1I9ok+h74q0ZdxkwM3EODqrs4pasq7O0iUxbcS9VtWMicG7f3+HAj0r1+NtKSw==}
+ engines: {node: '>=16.14.0'}
+ hasBin: true
+ peerDependencies:
+ '@opentelemetry/api': ^1.1.0
+ react: ^18.2.0
+ react-dom: ^18.2.0
+ sass: ^1.3.0
+ peerDependenciesMeta:
+ '@opentelemetry/api':
+ optional: true
+ sass:
+ optional: true
+ dependencies:
+ '@next/env': 13.5.6
+ '@swc/helpers': 0.5.2
+ busboy: 1.6.0
+ caniuse-lite: 1.0.30001579
+ postcss: 8.4.31
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ styled-jsx: 5.1.1(@babel/core@7.23.9)(react@18.2.0)
+ watchpack: 2.4.0
+ optionalDependencies:
+ '@next/swc-darwin-arm64': 13.5.6
+ '@next/swc-darwin-x64': 13.5.6
+ '@next/swc-linux-arm64-gnu': 13.5.6
+ '@next/swc-linux-arm64-musl': 13.5.6
+ '@next/swc-linux-x64-gnu': 13.5.6
+ '@next/swc-linux-x64-musl': 13.5.6
+ '@next/swc-win32-arm64-msvc': 13.5.6
+ '@next/swc-win32-ia32-msvc': 13.5.6
+ '@next/swc-win32-x64-msvc': 13.5.6
+ transitivePeerDependencies:
+ - '@babel/core'
+ - babel-plugin-macros
+ dev: true
+
+ /next@14.1.0(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-wlzrsbfeSU48YQBjZhDzOwhWhGsy+uQycR8bHAOt1LY1bn3zZEcDyHQOEoN3aWzQ8LHCAJ1nqrWCc9XF2+O45Q==}
+ engines: {node: '>=18.17.0'}
+ hasBin: true
+ peerDependencies:
+ '@opentelemetry/api': ^1.1.0
+ react: ^18.2.0
+ react-dom: ^18.2.0
+ sass: ^1.3.0
+ peerDependenciesMeta:
+ '@opentelemetry/api':
+ optional: true
+ sass:
+ optional: true
+ dependencies:
+ '@next/env': 14.1.0
+ '@swc/helpers': 0.5.2
+ busboy: 1.6.0
+ caniuse-lite: 1.0.30001579
+ graceful-fs: 4.2.11
+ postcss: 8.4.31
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ styled-jsx: 5.1.1(@babel/core@7.23.9)(react@18.2.0)
+ optionalDependencies:
+ '@next/swc-darwin-arm64': 14.1.0
+ '@next/swc-darwin-x64': 14.1.0
+ '@next/swc-linux-arm64-gnu': 14.1.0
+ '@next/swc-linux-arm64-musl': 14.1.0
+ '@next/swc-linux-x64-gnu': 14.1.0
+ '@next/swc-linux-x64-musl': 14.1.0
+ '@next/swc-win32-arm64-msvc': 14.1.0
+ '@next/swc-win32-ia32-msvc': 14.1.0
+ '@next/swc-win32-x64-msvc': 14.1.0
+ transitivePeerDependencies:
+ - '@babel/core'
+ - babel-plugin-macros
+ dev: false
+
+ /nextra-theme-blog@2.13.4(next@14.1.0)(nextra@2.13.4)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-ORVWFW59WY1megGZZtzZE05wa6G24PAYGnKe+H7/xf3TFonOkZvOc+NTJc9res/A1/rRtgkkTz1r24CAhTHAyA==}
+ peerDependencies:
+ next: '>=9.5.3'
+ nextra: 2.13.4
+ react: '>=16.13.1'
+ react-cusdis: ^2.1.3
+ react-dom: '>=16.13.1'
+ peerDependenciesMeta:
+ react-cusdis:
+ optional: true
+ dependencies:
+ next: 14.1.0(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0)
+ next-themes: 0.2.1(next@14.1.0)(react-dom@18.2.0)(react@18.2.0)
+ nextra: 2.13.4(next@14.1.0)(react-dom@18.2.0)(react@18.2.0)
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /nextra@2.13.4(next@14.1.0)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-7of2rSBxuUa3+lbMmZwG9cqgftcoNOVQLTT6Rxf3EhBR9t1EI7b43dted8YoqSNaigdE3j1CoyNkX8N/ZzlEpw==}
+ engines: {node: '>=16'}
+ peerDependencies:
+ next: '>=9.5.3'
+ react: '>=16.13.1'
+ react-dom: '>=16.13.1'
+ dependencies:
+ '@headlessui/react': 1.7.18(react-dom@18.2.0)(react@18.2.0)
+ '@mdx-js/mdx': 2.3.0
+ '@mdx-js/react': 2.3.0(react@18.2.0)
+ '@napi-rs/simple-git': 0.1.16
+ '@theguild/remark-mermaid': 0.0.5(react@18.2.0)
+ '@theguild/remark-npm2yarn': 0.2.1
+ clsx: 2.1.0
+ github-slugger: 2.0.0
+ graceful-fs: 4.2.11
+ gray-matter: 4.0.3
+ katex: 0.16.9
+ lodash.get: 4.4.2
+ next: 14.1.0(@babel/core@7.23.9)(react-dom@18.2.0)(react@18.2.0)
+ next-mdx-remote: 4.4.1(react-dom@18.2.0)(react@18.2.0)
+ p-limit: 3.1.0
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ rehype-katex: 7.0.0
+ rehype-pretty-code: 0.9.11(shiki@0.14.7)
+ rehype-raw: 7.0.0
+ remark-gfm: 3.0.1
+ remark-math: 5.1.1
+ remark-reading-time: 2.0.1
+ shiki: 0.14.7
+ slash: 3.0.0
+ title: 3.5.3
+ unist-util-remove: 4.0.0
+ unist-util-visit: 5.0.0
+ zod: 3.22.4
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
+ /node-abi@3.65.0:
+ resolution: {integrity: sha512-ThjYBfoDNr08AWx6hGaRbfPwxKV9kVzAzOzlLKbk2CuqXE2xnCh+cbAGnwM3t8Lq4v9rUB7VfondlkBckcJrVA==}
+ engines: {node: '>=10'}
+ dependencies:
+ semver: 7.5.4
+ dev: false
+
+ /node-addon-api@6.1.0:
+ resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==}
+ dev: false
+
+ /node-releases@2.0.14:
+ resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==}
+
+ /nodemailer@6.9.8:
+ resolution: {integrity: sha512-cfrYUk16e67Ks051i4CntM9kshRYei1/o/Gi8K1d+R34OIs21xdFnW7Pt7EucmVKA0LKtqUGNcjMZ7ehjl49mQ==}
+ engines: {node: '>=6.0.0'}
+ dev: false
+
+ /non-layered-tidy-tree-layout@2.0.2:
+ resolution: {integrity: sha512-gkXMxRzUH+PB0ax9dUN0yYF0S25BqeAYqhgMaLUFmpXLEk7Fcu8f4emJuOAY0V8kjDICxROIKsTAKsV/v355xw==}
+ dev: false
+
+ /nopt@7.2.0:
+ resolution: {integrity: sha512-CVDtwCdhYIvnAzFoJ6NJ6dX3oga9/HyciQDnG1vQDjSLMeKLJ4A93ZqYKDrgYSr1FBY5/hMYC+2VCi24pgpkGA==}
+ engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
+ hasBin: true
+ dependencies:
+ abbrev: 2.0.0
+ dev: false
+
+ /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: true
+
+ /npm-run-path@2.0.2:
+ resolution: {integrity: sha512-lJxZYlT4DW/bRUtFh1MQIWqmLwQfAxnqWG4HhEdjMlkrJYnJn0Jrr2u3mgxqaWsdiBc76TYkTG/mhrnYTuzfHw==}
+ engines: {node: '>=4'}
+ dependencies:
+ path-key: 2.0.1
+ dev: false
+
+ /npm-to-yarn@2.2.1:
+ resolution: {integrity: sha512-O/j/ROyX0KGLG7O6Ieut/seQ0oiTpHF2tXAcFbpdTLQFiaNtkyTXXocM1fwpaa60dg1qpWj0nHlbNhx6qwuENQ==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ dev: false
+
+ /oauth@0.9.15:
+ resolution: {integrity: sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==}
+ dev: false
+
+ /object-assign@4.1.1:
+ resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+ engines: {node: '>=0.10.0'}
+
+ /object-hash@2.2.0:
+ resolution: {integrity: sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==}
+ engines: {node: '>= 6'}
+ dev: false
+
+ /object-hash@3.0.0:
+ resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==}
+ engines: {node: '>= 6'}
+
+ /object-inspect@1.13.1:
+ resolution: {integrity: sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==}
+
+ /object-keys@1.1.1:
+ resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
+ engines: {node: '>= 0.4'}
+
+ /object.assign@4.1.5:
+ resolution: {integrity: sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.5
+ define-properties: 1.2.1
+ has-symbols: 1.0.3
+ object-keys: 1.1.1
+
+ /object.entries@1.1.7:
+ resolution: {integrity: sha512-jCBs/0plmPsOnrKAfFQXRG2NFjlhZgjjcBLSmTnEhU8U6vVTsVe8ANeQJCHTl3gSsI4J+0emOoCgoKlmQPMgmA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.5
+ define-properties: 1.2.1
+ es-abstract: 1.22.3
+ dev: true
+
+ /object.fromentries@2.0.7:
+ resolution: {integrity: sha512-UPbPHML6sL8PI/mOqPwsH4G6iyXcCGzLin8KvEPenOZN5lpCNBZZQ+V62vdjB1mQHrmqGQt5/OJzemUA+KJmEA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.5
+ define-properties: 1.2.1
+ es-abstract: 1.22.3
+ dev: true
+
+ /object.groupby@1.0.1:
+ resolution: {integrity: sha512-HqaQtqLnp/8Bn4GL16cj+CUYbnpe1bh0TtEaWvybszDG4tgxCJuRpV8VGuvNaI1fAnI4lUJzDG55MXcOH4JZcQ==}
+ dependencies:
+ call-bind: 1.0.5
+ define-properties: 1.2.1
+ es-abstract: 1.22.3
+ get-intrinsic: 1.2.2
+ dev: true
+
+ /object.hasown@1.1.3:
+ resolution: {integrity: sha512-fFI4VcYpRHvSLXxP7yiZOMAd331cPfd2p7PFDVbgUsYOfCT3tICVqXWngbjr4m49OvsBwUBQ6O2uQoJvy3RexA==}
+ dependencies:
+ define-properties: 1.2.1
+ es-abstract: 1.22.3
+ dev: true
+
+ /object.values@1.1.7:
+ resolution: {integrity: sha512-aU6xnDFYT3x17e/f0IiiwlGPTy2jzMySGfUB4fq6z7CV8l85CWHDk5ErhyhpfDHhrOMwGFhSQkhMGHaIotA6Ng==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.5
+ define-properties: 1.2.1
+ es-abstract: 1.22.3
+ dev: true
+
+ /oidc-token-hash@5.0.3:
+ resolution: {integrity: sha512-IF4PcGgzAr6XXSff26Sk/+P4KZFJVuHAJZj3wgO3vX2bMdNVp/QXTP3P7CEm9V1IdG8lDLY3HhiqpsE/nOwpPw==}
+ engines: {node: ^10.13.0 || >=12.0.0}
+ dev: false
+
+ /once@1.4.0:
+ resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+ dependencies:
+ wrappy: 1.0.2
+
+ /openid-client@5.6.4:
+ resolution: {integrity: sha512-T1h3B10BRPKfcObdBklX639tVz+xh34O7GjofqrqiAQdm7eHsQ00ih18x6wuJ/E6FxdtS2u3FmUGPDeEcMwzNA==}
+ dependencies:
+ jose: 4.15.4
+ lru-cache: 6.0.0
+ object-hash: 2.2.0
+ oidc-token-hash: 5.0.3
+ dev: false
+
+ /optionator@0.9.3:
+ resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
+ engines: {node: '>= 0.8.0'}
+ dependencies:
+ '@aashutoshrathi/word-wrap': 1.2.6
+ deep-is: 0.1.4
+ fast-levenshtein: 2.0.6
+ levn: 0.4.1
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+ dev: true
+
+ /p-finally@1.0.0:
+ resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==}
+ engines: {node: '>=4'}
+ dev: false
+
+ /p-limit@2.3.0:
+ resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
+ engines: {node: '>=6'}
+ dependencies:
+ p-try: 2.2.0
+ dev: false
+
+ /p-limit@3.1.0:
+ resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+ engines: {node: '>=10'}
+ dependencies:
+ yocto-queue: 0.1.0
+
+ /p-limit@4.0.0:
+ resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ dependencies:
+ yocto-queue: 1.0.0
+ dev: false
+
+ /p-locate@4.1.0:
+ resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
+ engines: {node: '>=8'}
+ dependencies:
+ p-limit: 2.3.0
+ dev: false
+
+ /p-locate@5.0.0:
+ resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+ engines: {node: '>=10'}
+ dependencies:
+ p-limit: 3.1.0
+ dev: true
+
+ /p-locate@6.0.0:
+ resolution: {integrity: sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ dependencies:
+ p-limit: 4.0.0
+ dev: false
+
+ /p-map@2.1.0:
+ resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==}
+ engines: {node: '>=6'}
+ dev: false
+
+ /p-try@2.2.0:
+ resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
+ engines: {node: '>=6'}
+ dev: false
+
+ /parent-module@1.0.1:
+ resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+ engines: {node: '>=6'}
+ dependencies:
+ callsites: 3.1.0
+ dev: true
+
+ /parse-entities@4.0.1:
+ resolution: {integrity: sha512-SWzvYcSJh4d/SGLIOQfZ/CoNv6BTlI6YEQ7Nj82oDVnRpwe/Z/F1EMx42x3JAOwGBlCjeCH0BRJQbQ/opHL17w==}
+ dependencies:
+ '@types/unist': 2.0.10
+ character-entities: 2.0.2
+ character-entities-legacy: 3.0.0
+ character-reference-invalid: 2.0.1
+ decode-named-character-reference: 1.0.2
+ is-alphanumerical: 2.0.1
+ is-decimal: 2.0.1
+ is-hexadecimal: 2.0.1
+ dev: false
+
+ /parse-numeric-range@1.3.0:
+ resolution: {integrity: sha512-twN+njEipszzlMJd4ONUYgSfZPDxgHhT9Ahed5uTigpQn90FggW4SA/AIPq/6a149fTbE9qBEcSwE3FAEp6wQQ==}
+ dev: false
+
+ /parse5@7.1.2:
+ resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==}
+ dependencies:
+ entities: 4.5.0
+ dev: false
+
+ /parseley@0.12.1:
+ resolution: {integrity: sha512-e6qHKe3a9HWr0oMRVDTRhKce+bRO8VGQR3NyVwcjwrbhMmFCX9KszEV35+rn4AdilFAq9VPxP/Fe1wC9Qjd2lw==}
+ dependencies:
+ leac: 0.6.0
+ peberminta: 0.9.0
+ dev: false
+
+ /path-exists@4.0.0:
+ resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+ engines: {node: '>=8'}
+
+ /path-exists@5.0.0:
+ resolution: {integrity: sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==}
+ engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+ dev: false
+
+ /path-is-absolute@1.0.1:
+ resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
+ engines: {node: '>=0.10.0'}
+
+ /path-is-inside@1.0.2:
+ resolution: {integrity: sha512-DUWJr3+ULp4zXmol/SZkFf3JGsS9/SIv+Y3Rt93/UjPpDpklB5f1er4O3POIbUuUJ3FXgqte2Q7SrU6zAqwk8w==}
+ dev: false
+
+ /path-key@2.0.1:
+ resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==}
+ engines: {node: '>=4'}
+ dev: false
+
+ /path-key@3.1.1:
+ resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+ engines: {node: '>=8'}
+
+ /path-parse@1.0.7:
+ resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+
+ /path-scurry@1.10.1:
+ resolution: {integrity: sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==}
+ engines: {node: '>=16 || 14 >=14.17'}
+ dependencies:
+ lru-cache: 10.1.0
+ minipass: 7.0.4
+
+ /path-type@4.0.0:
+ resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
+ engines: {node: '>=8'}
+
+ /peberminta@0.9.0:
+ resolution: {integrity: sha512-XIxfHpEuSJbITd1H3EeQwpcZbTLHc+VVr8ANI9t5sit565tsI4/xK3KWTUFE2e6QiangUkh3B0jihzmGnNrRsQ==}
+ dev: false
+
+ /periscopic@3.1.0:
+ resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==}
+ dependencies:
+ '@types/estree': 1.0.5
+ estree-walker: 3.0.3
+ is-reference: 3.0.2
+ 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'}
+
+ /pify@2.3.0:
+ resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
+ engines: {node: '>=0.10.0'}
+
+ /pify@4.0.1:
+ resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
+ engines: {node: '>=6'}
+ dev: false
+
+ /pinkie-promise@2.0.1:
+ resolution: {integrity: sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ pinkie: 2.0.4
+ dev: false
+
+ /pinkie@2.0.4:
+ resolution: {integrity: sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==}
+ engines: {node: '>=0.10.0'}
+ dev: false
+
+ /pirates@4.0.6:
+ resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==}
+ engines: {node: '>= 6'}
+
+ /pkg-dir@4.2.0:
+ resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
+ engines: {node: '>=8'}
+ dependencies:
+ find-up: 4.1.0
+ dev: false
+
+ /pkg-dir@7.0.0:
+ resolution: {integrity: sha512-Ie9z/WINcxxLp27BKOCHGde4ITq9UklYKDzVo1nhk5sqGEXU3FpkwP5GM2voTGJkGd9B3Otl+Q4uwSOeSUtOBA==}
+ engines: {node: '>=14.16'}
+ dependencies:
+ find-up: 6.3.0
+ dev: false
+
+ /postcss-import@15.1.0(postcss@8.4.33):
+ resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
+ engines: {node: '>=14.0.0'}
+ peerDependencies:
+ postcss: ^8.0.0
+ dependencies:
+ postcss: 8.4.33
+ postcss-value-parser: 4.2.0
+ read-cache: 1.0.0
+ resolve: 1.22.8
+
+ /postcss-js@4.0.1(postcss@8.4.33):
+ resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==}
+ engines: {node: ^12 || ^14 || >= 16}
+ peerDependencies:
+ postcss: ^8.4.21
+ dependencies:
+ camelcase-css: 2.0.1
+ postcss: 8.4.33
+
+ /postcss-load-config@4.0.2(postcss@8.4.33):
+ resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==}
+ engines: {node: '>= 14'}
+ peerDependencies:
+ postcss: '>=8.0.9'
+ ts-node: '>=9.0.0'
+ peerDependenciesMeta:
+ postcss:
+ optional: true
+ ts-node:
+ optional: true
+ dependencies:
+ lilconfig: 3.0.0
+ postcss: 8.4.33
+ yaml: 2.3.4
+
+ /postcss-nested@6.0.1(postcss@8.4.33):
+ resolution: {integrity: sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==}
+ engines: {node: '>=12.0'}
+ peerDependencies:
+ postcss: ^8.2.14
+ dependencies:
+ postcss: 8.4.33
+ postcss-selector-parser: 6.0.15
+
+ /postcss-selector-parser@6.0.15:
+ resolution: {integrity: sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==}
+ engines: {node: '>=4'}
+ dependencies:
+ cssesc: 3.0.0
+ util-deprecate: 1.0.2
+
+ /postcss-value-parser@4.2.0:
+ resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
+
+ /postcss@8.4.31:
+ resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
+ engines: {node: ^10 || ^12 || >=14}
+ dependencies:
+ nanoid: 3.3.7
+ picocolors: 1.0.0
+ source-map-js: 1.0.2
+
+ /postcss@8.4.33:
+ resolution: {integrity: sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==}
+ engines: {node: ^10 || ^12 || >=14}
+ dependencies:
+ nanoid: 3.3.7
+ picocolors: 1.0.0
+ source-map-js: 1.0.2
+
+ /preact-render-to-string@5.2.6(preact@10.19.3):
+ resolution: {integrity: sha512-JyhErpYOvBV1hEPwIxc/fHWXPfnEGdRKxc8gFdAZ7XV4tlzyzG847XAyEZqoDnynP88akM4eaHcSOzNcLWFguw==}
+ peerDependencies:
+ preact: '>=10'
+ dependencies:
+ preact: 10.19.3
+ pretty-format: 3.8.0
+ dev: false
+
+ /preact@10.19.3:
+ resolution: {integrity: sha512-nHHTeFVBTHRGxJXKkKu5hT8C/YWBkPso4/Gad6xuj5dbptt9iF9NZr9pHbPhBrnT2klheu7mHTxTZ/LjwJiEiQ==}
+ dev: false
+
+ /prebuild-install@7.1.2:
+ resolution: {integrity: sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==}
+ engines: {node: '>=10'}
+ hasBin: true
+ dependencies:
+ detect-libc: 2.0.2
+ expand-template: 2.0.3
+ github-from-package: 0.0.0
+ minimist: 1.2.8
+ mkdirp-classic: 0.5.3
+ napi-build-utils: 1.0.2
+ node-abi: 3.65.0
+ pump: 3.0.0
+ rc: 1.2.8
+ simple-get: 4.0.1
+ tar-fs: 2.1.1
+ tunnel-agent: 0.6.0
+ dev: false
+
+ /prelude-ls@1.2.1:
+ resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+ engines: {node: '>= 0.8.0'}
+ dev: true
+
+ /prettier-plugin-tailwindcss@0.5.11(prettier@3.2.4):
+ resolution: {integrity: sha512-AvI/DNyMctyyxGOjyePgi/gqj5hJYClZ1avtQvLlqMT3uDZkRbi4HhGUpok3DRzv9z7Lti85Kdj3s3/1CeNI0w==}
+ engines: {node: '>=14.21.3'}
+ peerDependencies:
+ '@ianvs/prettier-plugin-sort-imports': '*'
+ '@prettier/plugin-pug': '*'
+ '@shopify/prettier-plugin-liquid': '*'
+ '@trivago/prettier-plugin-sort-imports': '*'
+ prettier: ^3.0
+ prettier-plugin-astro: '*'
+ prettier-plugin-css-order: '*'
+ prettier-plugin-import-sort: '*'
+ prettier-plugin-jsdoc: '*'
+ prettier-plugin-marko: '*'
+ prettier-plugin-organize-attributes: '*'
+ prettier-plugin-organize-imports: '*'
+ prettier-plugin-style-order: '*'
+ prettier-plugin-svelte: '*'
+ prettier-plugin-twig-melody: '*'
+ peerDependenciesMeta:
+ '@ianvs/prettier-plugin-sort-imports':
+ optional: true
+ '@prettier/plugin-pug':
+ optional: true
+ '@shopify/prettier-plugin-liquid':
+ optional: true
+ '@trivago/prettier-plugin-sort-imports':
+ optional: true
+ prettier-plugin-astro:
+ optional: true
+ prettier-plugin-css-order:
+ optional: true
+ prettier-plugin-import-sort:
+ optional: true
+ prettier-plugin-jsdoc:
+ optional: true
+ prettier-plugin-marko:
+ optional: true
+ prettier-plugin-organize-attributes:
+ optional: true
+ prettier-plugin-organize-imports:
+ optional: true
+ prettier-plugin-style-order:
+ optional: true
+ prettier-plugin-svelte:
+ optional: true
+ prettier-plugin-twig-melody:
+ optional: true
+ dependencies:
+ prettier: 3.2.4
+ dev: true
+
+ /prettier@3.2.4:
+ resolution: {integrity: sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==}
+ engines: {node: '>=14'}
+ hasBin: true
+ dev: true
+
+ /pretty-bytes@5.6.0:
+ resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==}
+ engines: {node: '>=6'}
+
+ /pretty-format@3.8.0:
+ resolution: {integrity: sha512-WuxUnVtlWL1OfZFQFuqvnvs6MiAGk9UNsBostyBOB0Is9wb5uRESevA6rnl/rkksXaGX3GzZhPup5d6Vp1nFew==}
+ dev: false
+
+ /prisma@5.9.1:
+ resolution: {integrity: sha512-Hy/8KJZz0ELtkw4FnG9MS9rNWlXcJhf98Z2QMqi0QiVMoS8PzsBkpla0/Y5hTlob8F3HeECYphBjqmBxrluUrQ==}
+ engines: {node: '>=16.13'}
+ hasBin: true
+ requiresBuild: true
+ dependencies:
+ '@prisma/engines': 5.9.1
+
+ /prop-types@15.8.1:
+ resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
+ dependencies:
+ loose-envify: 1.4.0
+ object-assign: 4.1.1
+ react-is: 16.13.1
+ dev: true
+
+ /property-information@6.4.1:
+ resolution: {integrity: sha512-OHYtXfu5aI2sS2LWFSN5rgJjrQ4pCy8i1jubJLe2QvMF8JJ++HXTUIVWFLfXJoaOfvYYjk2SN8J2wFUWIGXT4w==}
+ dev: false
+
+ /proto-list@1.2.4:
+ resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==}
+ dev: false
+
+ /pseudomap@1.0.2:
+ resolution: {integrity: sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==}
+ dev: false
+
+ /pump@3.0.0:
+ resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
+ dependencies:
+ end-of-stream: 1.4.4
+ once: 1.4.0
+ dev: false
+
+ /punycode@2.3.1:
+ resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+ engines: {node: '>=6'}
+
+ /queue-microtask@1.2.3:
+ resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
+ /queue-tick@1.0.1:
+ resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==}
+ dev: false
+
+ /randombytes@2.1.0:
+ resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==}
+ dependencies:
+ safe-buffer: 5.2.1
+
+ /rc@1.2.8:
+ resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==}
+ hasBin: true
+ dependencies:
+ deep-extend: 0.6.0
+ ini: 1.3.8
+ minimist: 1.2.8
+ strip-json-comments: 2.0.1
+ dev: false
+
+ /react-day-picker@8.10.0(date-fns@3.3.1)(react@18.2.0):
+ resolution: {integrity: sha512-mz+qeyrOM7++1NCb1ARXmkjMkzWVh2GL9YiPbRjKe0zHccvekk4HE+0MPOZOrosn8r8zTHIIeOUXTmXRqmkRmg==}
+ peerDependencies:
+ date-fns: ^2.28.0 || ^3.0.0
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ dependencies:
+ date-fns: 3.3.1
+ react: 18.2.0
+ dev: false
+
+ /react-dom@18.2.0(react@18.2.0):
+ resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==}
+ peerDependencies:
+ react: ^18.2.0
+ dependencies:
+ loose-envify: 1.4.0
+ react: 18.2.0
+ scheduler: 0.23.0
+
+ /react-hook-form@7.50.1(react@18.2.0):
+ resolution: {integrity: sha512-3PCY82oE0WgeOgUtIr3nYNNtNvqtJ7BZjsbxh6TnYNbXButaD5WpjOmTjdxZfheuHKR68qfeFnEDVYoSSFPMTQ==}
+ engines: {node: '>=12.22.0'}
+ peerDependencies:
+ react: ^16.8.0 || ^17 || ^18
+ dependencies:
+ react: 18.2.0
+ dev: false
+
+ /react-is@16.13.1:
+ resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
+ dev: true
+
+ /react-remove-scroll-bar@2.3.4(@types/react@18.2.48)(react@18.2.0):
+ resolution: {integrity: sha512-63C4YQBUt0m6ALadE9XV56hV8BgJWDmmTPY758iIJjfQKt2nYwoUrPk0LXRXcB/yIj82T1/Ixfdpdk68LwIB0A==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@types/react': 18.2.48
+ react: 18.2.0
+ react-style-singleton: 2.2.1(@types/react@18.2.48)(react@18.2.0)
+ tslib: 2.6.2
+ dev: false
+
+ /react-remove-scroll@2.5.4(@types/react@18.2.48)(react@18.2.0):
+ resolution: {integrity: sha512-xGVKJJr0SJGQVirVFAUZ2k1QLyO6m+2fy0l8Qawbp5Jgrv3DeLalrfMNBFSlmz5kriGGzsVBtGVnf4pTKIhhWA==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@types/react': 18.2.48
+ react: 18.2.0
+ react-remove-scroll-bar: 2.3.4(@types/react@18.2.48)(react@18.2.0)
+ react-style-singleton: 2.2.1(@types/react@18.2.48)(react@18.2.0)
+ tslib: 2.6.2
+ use-callback-ref: 1.3.1(@types/react@18.2.48)(react@18.2.0)
+ use-sidecar: 1.1.2(@types/react@18.2.48)(react@18.2.0)
+ dev: false
+
+ /react-remove-scroll@2.5.5(@types/react@18.2.48)(react@18.2.0):
+ resolution: {integrity: sha512-ImKhrzJJsyXJfBZ4bzu8Bwpka14c/fQt0k+cyFp/PBhTfyDnU5hjOtM4AG/0AMyy8oKzOTR0lDgJIM7pYXI0kw==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@types/react': 18.2.48
+ react: 18.2.0
+ react-remove-scroll-bar: 2.3.4(@types/react@18.2.48)(react@18.2.0)
+ react-style-singleton: 2.2.1(@types/react@18.2.48)(react@18.2.0)
+ tslib: 2.6.2
+ use-callback-ref: 1.3.1(@types/react@18.2.48)(react@18.2.0)
+ use-sidecar: 1.1.2(@types/react@18.2.48)(react@18.2.0)
+ dev: false
+
+ /react-style-singleton@2.2.1(@types/react@18.2.48)(react@18.2.0):
+ resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@types/react': 18.2.48
+ get-nonce: 1.0.1
+ invariant: 2.2.4
+ react: 18.2.0
+ tslib: 2.6.2
+ dev: false
+
+ /react@18.2.0:
+ resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ loose-envify: 1.4.0
+
+ /read-cache@1.0.0:
+ resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
+ dependencies:
+ pify: 2.3.0
+
+ /readable-stream@3.6.2:
+ resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
+ engines: {node: '>= 6'}
+ dependencies:
+ inherits: 2.0.4
+ string_decoder: 1.3.0
+ util-deprecate: 1.0.2
+ dev: false
+
+ /readdirp@3.6.0:
+ resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+ engines: {node: '>=8.10.0'}
+ dependencies:
+ picomatch: 2.3.1
+
+ /reading-time@1.5.0:
+ resolution: {integrity: sha512-onYyVhBNr4CmAxFsKS7bz+uTLRakypIe4R+5A824vBSkQy/hB3fZepoVEf8OVAxzLvK+H/jm9TzpI3ETSm64Kg==}
+ dev: false
+
+ /reflect.getprototypeof@1.0.4:
+ resolution: {integrity: sha512-ECkTw8TmJwW60lOTR+ZkODISW6RQ8+2CL3COqtiJKLd6MmB45hN51HprHFziKLGkAuTGQhBb91V8cy+KHlaCjw==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.5
+ define-properties: 1.2.1
+ es-abstract: 1.22.3
+ get-intrinsic: 1.2.2
+ globalthis: 1.0.3
+ which-builtin-type: 1.1.3
+ dev: true
+
+ /regenerate-unicode-properties@10.1.1:
+ resolution: {integrity: sha512-X007RyZLsCJVVrjgEFVpLUTZwyOZk3oiL75ZcuYjlIWd6rNJtOjkBwQc5AsRrpbKVkxN6sklw/k/9m2jJYOf8Q==}
+ engines: {node: '>=4'}
+ dependencies:
+ regenerate: 1.4.2
+
+ /regenerate@1.4.2:
+ resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==}
+
+ /regenerator-runtime@0.14.1:
+ resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==}
+
+ /regenerator-transform@0.15.2:
+ resolution: {integrity: sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg==}
+ dependencies:
+ '@babel/runtime': 7.23.8
+
+ /regexp.prototype.flags@1.5.1:
+ resolution: {integrity: sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.5
+ define-properties: 1.2.1
+ set-function-name: 2.0.1
+
+ /regexpu-core@5.3.2:
+ resolution: {integrity: sha512-RAM5FlZz+Lhmo7db9L298p2vHP5ZywrVXmVXpmAD9GuL5MPH6t9ROw1iA/wfHkQ76Qe7AaPF0nGuim96/IrQMQ==}
+ engines: {node: '>=4'}
+ dependencies:
+ '@babel/regjsgen': 0.8.0
+ regenerate: 1.4.2
+ regenerate-unicode-properties: 10.1.1
+ regjsparser: 0.9.1
+ unicode-match-property-ecmascript: 2.0.0
+ unicode-match-property-value-ecmascript: 2.1.0
+
+ /regjsparser@0.9.1:
+ resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==}
+ hasBin: true
+ dependencies:
+ jsesc: 0.5.0
+
+ /rehype-katex@7.0.0:
+ resolution: {integrity: sha512-h8FPkGE00r2XKU+/acgqwWUlyzve1IiOKwsEkg4pDL3k48PiE0Pt+/uLtVHDVkN1yA4iurZN6UES8ivHVEQV6Q==}
+ dependencies:
+ '@types/hast': 3.0.4
+ '@types/katex': 0.16.7
+ hast-util-from-html-isomorphic: 2.0.0
+ hast-util-to-text: 4.0.0
+ katex: 0.16.9
+ unist-util-visit-parents: 6.0.1
+ vfile: 6.0.1
+ dev: false
+
+ /rehype-pretty-code@0.9.11(shiki@0.14.7):
+ resolution: {integrity: sha512-Eq90eCYXQJISktfRZ8PPtwc5SUyH6fJcxS8XOMnHPUQZBtC6RYo67gGlley9X2nR8vlniPj0/7oCDEYHKQa/oA==}
+ engines: {node: '>=16'}
+ peerDependencies:
+ shiki: '*'
+ dependencies:
+ '@types/hast': 2.3.10
+ hash-obj: 4.0.0
+ parse-numeric-range: 1.3.0
+ shiki: 0.14.7
+ dev: false
+
+ /rehype-raw@7.0.0:
+ resolution: {integrity: sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==}
+ dependencies:
+ '@types/hast': 3.0.4
+ hast-util-raw: 9.0.2
+ vfile: 6.0.1
+ dev: false
+
+ /remark-gfm@3.0.1:
+ resolution: {integrity: sha512-lEFDoi2PICJyNrACFOfDD3JlLkuSbOa5Wd8EPt06HUdptv8Gn0bxYTdbU/XXQ3swAPkEaGxxPN9cbnMHvVu1Ig==}
+ dependencies:
+ '@types/mdast': 3.0.15
+ mdast-util-gfm: 2.0.2
+ micromark-extension-gfm: 2.0.3
+ unified: 10.1.2
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
+ /remark-math@5.1.1:
+ resolution: {integrity: sha512-cE5T2R/xLVtfFI4cCePtiRn+e6jKMtFDR3P8V3qpv8wpKjwvHoBA4eJzvX+nVrnlNy0911bdGmuspCSwetfYHw==}
+ dependencies:
+ '@types/mdast': 3.0.15
+ mdast-util-math: 2.0.2
+ micromark-extension-math: 2.1.2
+ unified: 10.1.2
+ dev: false
+
+ /remark-mdx@2.3.0:
+ resolution: {integrity: sha512-g53hMkpM0I98MU266IzDFMrTD980gNF3BJnkyFcmN+dD873mQeD5rdMO3Y2X+x8umQfbSE0PcoEDl7ledSA+2g==}
+ dependencies:
+ mdast-util-mdx: 2.0.1
+ micromark-extension-mdxjs: 1.0.1
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
+ /remark-parse@10.0.2:
+ resolution: {integrity: sha512-3ydxgHa/ZQzG8LvC7jTXccARYDcRld3VfcgIIFs7bI6vbRSxJJmzgLEIIoYKyrfhaY+ujuWaf/PJiMZXoiCXgw==}
+ dependencies:
+ '@types/mdast': 3.0.15
+ mdast-util-from-markdown: 1.3.1
+ unified: 10.1.2
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
+ /remark-reading-time@2.0.1:
+ resolution: {integrity: sha512-fy4BKy9SRhtYbEHvp6AItbRTnrhiDGbqLQTSYVbQPGuRCncU1ubSsh9p/W5QZSxtYcUXv8KGL0xBgPLyNJA1xw==}
+ dependencies:
+ estree-util-is-identifier-name: 2.1.0
+ estree-util-value-to-estree: 1.3.0
+ reading-time: 1.5.0
+ unist-util-visit: 3.1.0
+ dev: false
+
+ /remark-rehype@10.1.0:
+ resolution: {integrity: sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw==}
+ dependencies:
+ '@types/hast': 2.3.10
+ '@types/mdast': 3.0.15
+ mdast-util-to-hast: 12.3.0
+ unified: 10.1.2
+ dev: false
+
+ /require-from-string@2.0.2:
+ resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
+ engines: {node: '>=0.10.0'}
+
+ /resend@3.2.0:
+ resolution: {integrity: sha512-lDHhexiFYPoLXy7zRlJ8D5eKxoXy6Tr9/elN3+Vv7PkUoYuSSD1fpiIfa/JYXEWyiyN2UczkCTLpkT8dDPJ4Pg==}
+ engines: {node: '>=18'}
+ dependencies:
+ '@react-email/render': 0.0.12
+ dev: false
+
+ /resolve-from@4.0.0:
+ resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+ engines: {node: '>=4'}
+ dev: true
+
+ /resolve-pkg-maps@1.0.0:
+ resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
+ dev: true
+
+ /resolve@1.22.8:
+ resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==}
+ hasBin: true
+ dependencies:
+ is-core-module: 2.13.1
+ path-parse: 1.0.7
+ supports-preserve-symlinks-flag: 1.0.0
+
+ /resolve@2.0.0-next.5:
+ resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==}
+ hasBin: true
+ dependencies:
+ is-core-module: 2.13.1
+ path-parse: 1.0.7
+ supports-preserve-symlinks-flag: 1.0.0
+ dev: true
+
+ /reusify@1.0.4:
+ resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
+ engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
+ /rimraf@2.7.1:
+ resolution: {integrity: sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==}
+ hasBin: true
+ dependencies:
+ glob: 7.2.3
+ dev: false
+
+ /rimraf@3.0.2:
+ resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
+ hasBin: true
+ dependencies:
+ glob: 7.2.3
+ dev: true
+
+ /robust-predicates@3.0.2:
+ resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==}
+ dev: false
+
+ /rollup-plugin-terser@7.0.2(rollup@2.79.1):
+ resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==}
+ deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser
+ peerDependencies:
+ rollup: ^2.0.0
+ dependencies:
+ '@babel/code-frame': 7.23.5
+ jest-worker: 26.6.2
+ rollup: 2.79.1
+ serialize-javascript: 4.0.0
+ terser: 5.27.0
+
+ /rollup@2.79.1:
+ resolution: {integrity: sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==}
+ engines: {node: '>=10.0.0'}
+ hasBin: true
+ optionalDependencies:
+ fsevents: 2.3.3
+
+ /run-parallel@1.2.0:
+ resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+ dependencies:
+ queue-microtask: 1.2.3
+
+ /rw@1.3.3:
+ resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==}
+ dev: false
+
+ /sade@1.8.1:
+ resolution: {integrity: sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A==}
+ engines: {node: '>=6'}
+ dependencies:
+ mri: 1.2.0
+ dev: false
+
+ /safe-array-concat@1.1.0:
+ resolution: {integrity: sha512-ZdQ0Jeb9Ofti4hbt5lX3T2JcAamT9hfzYU1MNB+z/jaEbB6wfFfPIR/zEORmZqobkCCJhSjodobH6WHNmJ97dg==}
+ engines: {node: '>=0.4'}
+ dependencies:
+ call-bind: 1.0.5
+ get-intrinsic: 1.2.2
+ has-symbols: 1.0.3
+ isarray: 2.0.5
+
+ /safe-buffer@5.2.1:
+ resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+
+ /safe-regex-test@1.0.2:
+ resolution: {integrity: sha512-83S9w6eFq12BBIJYvjMux6/dkirb8+4zJRA9cxNBVb7Wq5fJBW+Xze48WqR8pxua7bDuAaaAxtVVd4Idjp1dBQ==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.5
+ get-intrinsic: 1.2.2
+ is-regex: 1.1.4
+
+ /safer-buffer@2.1.2:
+ resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+ dev: false
+
+ /scheduler@0.23.0:
+ resolution: {integrity: sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==}
+ dependencies:
+ loose-envify: 1.4.0
+
+ /schema-utils@2.7.1:
+ resolution: {integrity: sha512-SHiNtMOUGWBQJwzISiVYKu82GiV4QYGePp3odlY1tuKO7gPtphAT5R/py0fA6xtbgLL/RvtJZnU9b8s0F1q0Xg==}
+ engines: {node: '>= 8.9.0'}
+ dependencies:
+ '@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'}
+ dependencies:
+ '@types/json-schema': 7.0.15
+ ajv: 6.12.6
+ ajv-keywords: 3.5.2(ajv@6.12.6)
+ dev: false
+
+ /schema-utils@4.2.0:
+ resolution: {integrity: sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==}
+ engines: {node: '>= 12.13.0'}
+ dependencies:
+ '@types/json-schema': 7.0.15
+ ajv: 8.12.0
+ ajv-formats: 2.1.1(ajv@8.12.0)
+ ajv-keywords: 5.1.0(ajv@8.12.0)
+ dev: false
+
+ /section-matter@1.0.0:
+ resolution: {integrity: sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA==}
+ engines: {node: '>=4'}
+ dependencies:
+ extend-shallow: 2.0.1
+ kind-of: 6.0.3
+ dev: false
+
+ /selderee@0.11.0:
+ resolution: {integrity: sha512-5TF+l7p4+OsnP8BCCvSyZiSPc4x4//p5uPwK8TCnVPJYRmU2aYKMpOXvw8zM5a5JvuuCGN1jmsMwuU2W02ukfA==}
+ dependencies:
+ parseley: 0.12.1
+ dev: false
+
+ /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
+ dependencies:
+ lru-cache: 6.0.0
+
+ /serialize-javascript@4.0.0:
+ resolution: {integrity: sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==}
+ dependencies:
+ randombytes: 2.1.0
+
+ /serialize-javascript@6.0.2:
+ resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==}
+ dependencies:
+ randombytes: 2.1.0
+ dev: false
+
+ /set-function-length@1.2.0:
+ resolution: {integrity: sha512-4DBHDoyHlM1IRPGYcoxexgh67y4ueR53FKV1yyxwFMY7aCqcN/38M1+SwZ/qJQ8iLv7+ck385ot4CcisOAPT9w==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ define-data-property: 1.1.1
+ function-bind: 1.1.2
+ get-intrinsic: 1.2.2
+ gopd: 1.0.1
+ has-property-descriptors: 1.0.1
+
+ /set-function-name@2.0.1:
+ resolution: {integrity: sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ define-data-property: 1.1.1
+ functions-have-names: 1.2.3
+ has-property-descriptors: 1.0.1
+
+ /sharp@0.32.6:
+ resolution: {integrity: sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==}
+ engines: {node: '>=14.15.0'}
+ requiresBuild: true
+ dependencies:
+ color: 4.2.3
+ detect-libc: 2.0.2
+ node-addon-api: 6.1.0
+ prebuild-install: 7.1.2
+ semver: 7.5.4
+ simple-get: 4.0.1
+ tar-fs: 3.0.6
+ tunnel-agent: 0.6.0
+ dev: false
+
+ /shebang-command@1.2.0:
+ resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==}
+ engines: {node: '>=0.10.0'}
+ dependencies:
+ shebang-regex: 1.0.0
+ dev: false
+
+ /shebang-command@2.0.0:
+ resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+ engines: {node: '>=8'}
+ dependencies:
+ shebang-regex: 3.0.0
+
+ /shebang-regex@1.0.0:
+ resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==}
+ engines: {node: '>=0.10.0'}
+ dev: false
+
+ /shebang-regex@3.0.0:
+ resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+ engines: {node: '>=8'}
+
+ /shiki@0.14.7:
+ resolution: {integrity: sha512-dNPAPrxSc87ua2sKJ3H5dQ/6ZaY8RNnaAqK+t0eG7p0Soi2ydiqbGOTaZCqaYvA/uZYfS1LJnemt3Q+mSfcPCg==}
+ dependencies:
+ ansi-sequence-parser: 1.1.1
+ jsonc-parser: 3.2.1
+ vscode-oniguruma: 1.7.0
+ vscode-textmate: 8.0.0
+ dev: false
+
+ /side-channel@1.0.4:
+ resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
+ dependencies:
+ call-bind: 1.0.5
+ get-intrinsic: 1.2.2
+ object-inspect: 1.13.1
+
+ /signal-exit@3.0.7:
+ resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
+ dev: false
+
+ /signal-exit@4.1.0:
+ resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
+ engines: {node: '>=14'}
+
+ /simple-concat@1.0.1:
+ resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==}
+ dev: false
+
+ /simple-get@4.0.1:
+ resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==}
+ dependencies:
+ decompress-response: 6.0.0
+ once: 1.4.0
+ simple-concat: 1.0.1
+ dev: false
+
+ /simple-swizzle@0.2.2:
+ resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==}
+ dependencies:
+ is-arrayish: 0.3.2
+ dev: false
+
+ /slash@3.0.0:
+ resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
+ engines: {node: '>=8'}
+
+ /sonner@1.4.0(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-nvkTsIuOmi9e5Wz5If8ldasJjZNVfwiXYijBi2dbijvTQnQppvMcXTFNxL/NUFWlI2yJ1JX7TREDsg+gYm9WyA==}
+ peerDependencies:
+ react: ^18.0.0
+ react-dom: ^18.0.0
+ dependencies:
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
+ /sort-keys@5.0.0:
+ resolution: {integrity: sha512-Pdz01AvCAottHTPQGzndktFNdbRA75BgOfeT1hH+AMnJFv8lynkPi42rfeEhpx1saTEI3YNMWxfqu0sFD1G8pw==}
+ engines: {node: '>=12'}
+ dependencies:
+ is-plain-obj: 4.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-support@0.5.21:
+ resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
+ dependencies:
+ buffer-from: 1.1.2
+ source-map: 0.6.1
+
+ /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'}
+ dev: false
+
+ /source-map@0.8.0-beta.0:
+ resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==}
+ engines: {node: '>= 8'}
+ dependencies:
+ whatwg-url: 7.1.0
+
+ /sourcemap-codec@1.4.8:
+ resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
+ deprecated: Please use @jridgewell/sourcemap-codec instead
+
+ /space-separated-tokens@2.0.2:
+ resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==}
+ dev: false
+
+ /sprintf-js@1.0.3:
+ resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
+ dev: false
+
+ /streamsearch@1.1.0:
+ resolution: {integrity: sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==}
+ engines: {node: '>=10.0.0'}
+
+ /streamx@2.18.0:
+ resolution: {integrity: sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==}
+ dependencies:
+ fast-fifo: 1.3.2
+ queue-tick: 1.0.1
+ text-decoder: 1.1.1
+ optionalDependencies:
+ bare-events: 2.4.2
+ dev: false
+
+ /string-width@4.2.3:
+ resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+ engines: {node: '>=8'}
+ 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'}
+ dependencies:
+ eastasianwidth: 0.2.0
+ emoji-regex: 9.2.2
+ strip-ansi: 7.1.0
+
+ /string.prototype.matchall@4.0.10:
+ resolution: {integrity: sha512-rGXbGmOEosIQi6Qva94HUjgPs9vKW+dkG7Y8Q5O2OYkWL6wFaTRZO8zM4mhP94uX55wgyrXzfS2aGtGzUL7EJQ==}
+ dependencies:
+ call-bind: 1.0.5
+ define-properties: 1.2.1
+ es-abstract: 1.22.3
+ get-intrinsic: 1.2.2
+ has-symbols: 1.0.3
+ internal-slot: 1.0.6
+ regexp.prototype.flags: 1.5.1
+ set-function-name: 2.0.1
+ side-channel: 1.0.4
+
+ /string.prototype.trim@1.2.8:
+ resolution: {integrity: sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.5
+ define-properties: 1.2.1
+ es-abstract: 1.22.3
+
+ /string.prototype.trimend@1.0.7:
+ resolution: {integrity: sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==}
+ dependencies:
+ call-bind: 1.0.5
+ define-properties: 1.2.1
+ es-abstract: 1.22.3
+
+ /string.prototype.trimstart@1.0.7:
+ resolution: {integrity: sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==}
+ dependencies:
+ call-bind: 1.0.5
+ define-properties: 1.2.1
+ es-abstract: 1.22.3
+
+ /string_decoder@1.3.0:
+ resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==}
+ dependencies:
+ safe-buffer: 5.2.1
+ dev: false
+
+ /stringify-entities@4.0.3:
+ resolution: {integrity: sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g==}
+ dependencies:
+ character-entities-html4: 2.1.0
+ character-entities-legacy: 3.0.0
+ dev: false
+
+ /stringify-object@3.3.0:
+ resolution: {integrity: sha512-rHqiFh1elqCQ9WPLIC8I0Q/g/wj5J1eMkyoiD6eoQApWHP0FtlK7rqnhmabL5VUY9JQCcqwwvlOaSuutekgyrw==}
+ engines: {node: '>=4'}
+ dependencies:
+ get-own-enumerable-property-symbols: 3.0.2
+ is-obj: 1.0.1
+ is-regexp: 1.0.0
+
+ /strip-ansi@6.0.1:
+ resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+ engines: {node: '>=8'}
+ 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
+
+ /strip-bom-string@1.0.0:
+ resolution: {integrity: sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g==}
+ engines: {node: '>=0.10.0'}
+ dev: false
+
+ /strip-bom@3.0.0:
+ resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
+ engines: {node: '>=4'}
+ dev: true
+
+ /strip-comments@2.0.1:
+ resolution: {integrity: sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw==}
+ engines: {node: '>=10'}
+
+ /strip-eof@1.0.0:
+ resolution: {integrity: sha512-7FCwGGmx8mD5xQd3RPUvnSpUXHM3BWuzjtpD4TXsfcZ9EL4azvVVUscFYwD9nx8Kh+uCBC00XBtAykoMHwTh8Q==}
+ engines: {node: '>=0.10.0'}
+ dev: false
+
+ /strip-json-comments@2.0.1:
+ resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==}
+ engines: {node: '>=0.10.0'}
+ dev: false
+
+ /strip-json-comments@3.1.1:
+ resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+ engines: {node: '>=8'}
+ dev: true
+
+ /strnum@1.0.5:
+ resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==}
+ dev: false
+
+ /style-to-object@0.4.4:
+ resolution: {integrity: sha512-HYNoHZa2GorYNyqiCaBgsxvcJIn7OHq6inEga+E6Ke3m5JkoqpQbnFssk4jwe+K7AhGa2fcha4wSOf1Kn01dMg==}
+ dependencies:
+ inline-style-parser: 0.1.1
+ dev: false
+
+ /styled-jsx@5.1.1(@babel/core@7.23.9)(react@18.2.0):
+ resolution: {integrity: sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==}
+ engines: {node: '>= 12.0.0'}
+ peerDependencies:
+ '@babel/core': '*'
+ babel-plugin-macros: '*'
+ react: '>= 16.8.0 || 17.x.x || ^18.0.0-0'
+ peerDependenciesMeta:
+ '@babel/core':
+ optional: true
+ babel-plugin-macros:
+ optional: true
+ dependencies:
+ '@babel/core': 7.23.9
+ client-only: 0.0.1
+ react: 18.2.0
+
+ /stylis@4.3.1:
+ resolution: {integrity: sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ==}
+ dev: false
+
+ /sucrase@3.35.0:
+ resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==}
+ engines: {node: '>=16 || 14 >=14.17'}
+ hasBin: true
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.3
+ commander: 4.1.1
+ glob: 10.3.10
+ lines-and-columns: 1.2.4
+ mz: 2.7.0
+ pirates: 4.0.6
+ ts-interface-checker: 0.1.13
+
+ /superjson@2.2.1:
+ resolution: {integrity: sha512-8iGv75BYOa0xRJHK5vRLEjE2H/i4lulTjzpUXic3Eg8akftYjkmQDa8JARQ42rlczXyFR3IeRoeFCc7RxHsYZA==}
+ engines: {node: '>=16'}
+ dependencies:
+ copy-anything: 3.0.5
+ dev: false
+
+ /supports-color@4.5.0:
+ resolution: {integrity: sha512-ycQR/UbvI9xIlEdQT1TQqwoXtEldExbCEAJgRo5YXlmSKjv6ThHnP9/vwGa1gr19Gfw+LkFd7KqYMhzrRC5JYw==}
+ engines: {node: '>=4'}
+ dependencies:
+ has-flag: 2.0.0
+ dev: false
+
+ /supports-color@5.5.0:
+ resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
+ engines: {node: '>=4'}
+ dependencies:
+ has-flag: 3.0.0
+
+ /supports-color@7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+ dependencies:
+ has-flag: 4.0.0
+
+ /supports-color@8.1.1:
+ resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
+ engines: {node: '>=10'}
+ dependencies:
+ has-flag: 4.0.0
+ dev: false
+
+ /supports-preserve-symlinks-flag@1.0.0:
+ resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+ engines: {node: '>= 0.4'}
+
+ /tailwind-merge@2.2.0:
+ resolution: {integrity: sha512-SqqhhaL0T06SW59+JVNfAqKdqLs0497esifRrZ7jOaefP3o64fdFNDMrAQWZFMxTLJPiHVjRLUywT8uFz1xNWQ==}
+ dependencies:
+ '@babel/runtime': 7.23.8
+ dev: false
+
+ /tailwindcss-animate@1.0.7(tailwindcss@3.4.1):
+ resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==}
+ peerDependencies:
+ tailwindcss: '>=3.0.0 || insiders'
+ dependencies:
+ tailwindcss: 3.4.1
+ dev: false
+
+ /tailwindcss@3.4.1:
+ resolution: {integrity: sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==}
+ engines: {node: '>=14.0.0'}
+ hasBin: true
+ dependencies:
+ '@alloc/quick-lru': 5.2.0
+ arg: 5.0.2
+ chokidar: 3.5.3
+ didyoumean: 1.2.2
+ dlv: 1.1.3
+ fast-glob: 3.3.2
+ glob-parent: 6.0.2
+ is-glob: 4.0.3
+ jiti: 1.21.6
+ lilconfig: 2.1.0
+ micromatch: 4.0.5
+ normalize-path: 3.0.0
+ object-hash: 3.0.0
+ picocolors: 1.0.0
+ postcss: 8.4.33
+ postcss-import: 15.1.0(postcss@8.4.33)
+ postcss-js: 4.0.1(postcss@8.4.33)
+ postcss-load-config: 4.0.2(postcss@8.4.33)
+ postcss-nested: 6.0.1(postcss@8.4.33)
+ postcss-selector-parser: 6.0.15
+ resolve: 1.22.8
+ sucrase: 3.35.0
+ transitivePeerDependencies:
+ - ts-node
+
+ /tapable@2.2.1:
+ resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
+ engines: {node: '>=6'}
+
+ /tar-fs@2.1.1:
+ resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==}
+ dependencies:
+ chownr: 1.1.4
+ mkdirp-classic: 0.5.3
+ pump: 3.0.0
+ tar-stream: 2.2.0
+ dev: false
+
+ /tar-fs@3.0.6:
+ resolution: {integrity: sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==}
+ dependencies:
+ pump: 3.0.0
+ tar-stream: 3.1.7
+ optionalDependencies:
+ bare-fs: 2.3.1
+ bare-path: 2.1.3
+ dev: false
+
+ /tar-stream@2.2.0:
+ resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==}
+ engines: {node: '>=6'}
+ dependencies:
+ bl: 4.1.0
+ end-of-stream: 1.4.4
+ fs-constants: 1.0.0
+ inherits: 2.0.4
+ readable-stream: 3.6.2
+ dev: false
+
+ /tar-stream@3.1.7:
+ resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==}
+ dependencies:
+ b4a: 1.6.6
+ fast-fifo: 1.3.2
+ streamx: 2.18.0
+ dev: false
+
+ /temp-dir@2.0.0:
+ resolution: {integrity: sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==}
+ engines: {node: '>=8'}
+
+ /tempy@0.6.0:
+ resolution: {integrity: sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw==}
+ engines: {node: '>=10'}
+ dependencies:
+ is-stream: 2.0.1
+ temp-dir: 2.0.0
+ type-fest: 0.16.0
+ unique-string: 2.0.0
+
+ /terser-webpack-plugin@5.3.10(webpack@5.90.1):
+ resolution: {integrity: sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w==}
+ 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
+ dependencies:
+ '@jridgewell/trace-mapping': 0.3.22
+ jest-worker: 27.5.1
+ schema-utils: 3.3.0
+ serialize-javascript: 6.0.2
+ terser: 5.27.0
+ webpack: 5.90.1
+ dev: false
+
+ /terser@5.27.0:
+ resolution: {integrity: sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A==}
+ engines: {node: '>=10'}
+ hasBin: true
+ dependencies:
+ '@jridgewell/source-map': 0.3.5
+ acorn: 8.11.3
+ commander: 2.20.3
+ source-map-support: 0.5.21
+
+ /text-decoder@1.1.1:
+ resolution: {integrity: sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==}
+ dependencies:
+ b4a: 1.6.6
+ dev: false
+
+ /text-table@0.2.0:
+ resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
+ dev: true
+
+ /thenify-all@1.6.0:
+ resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==}
+ engines: {node: '>=0.8'}
+ dependencies:
+ thenify: 3.3.1
+
+ /thenify@3.3.1:
+ resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==}
+ dependencies:
+ any-promise: 1.3.0
+
+ /title@3.5.3:
+ resolution: {integrity: sha512-20JyowYglSEeCvZv3EZ0nZ046vLarO37prvV0mbtQV7C8DJPGgN967r8SJkqd3XK3K3lD3/Iyfp3avjfil8Q2Q==}
+ hasBin: true
+ dependencies:
+ arg: 1.0.0
+ chalk: 2.3.0
+ clipboardy: 1.2.2
+ titleize: 1.0.0
+ dev: false
+
+ /titleize@1.0.0:
+ resolution: {integrity: sha512-TARUb7z1pGvlLxgPk++7wJ6aycXF3GJ0sNSBTAsTuJrQG5QuZlkUQP+zl+nbjAh4gMX9yDw9ZYklMd7vAfJKEw==}
+ engines: {node: '>=0.10.0'}
+ dev: false
+
+ /to-fast-properties@2.0.0:
+ resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
+ engines: {node: '>=4'}
+
+ /to-regex-range@5.0.1:
+ resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+ engines: {node: '>=8.0'}
+ dependencies:
+ is-number: 7.0.0
+
+ /tr46@1.0.1:
+ resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==}
+ dependencies:
+ punycode: 2.3.1
+
+ /trim-lines@3.0.1:
+ resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
+ dev: false
+
+ /trough@2.2.0:
+ resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==}
+ dev: false
+
+ /ts-api-utils@1.0.3(typescript@5.3.3):
+ resolution: {integrity: sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==}
+ engines: {node: '>=16.13.0'}
+ peerDependencies:
+ typescript: '>=4.2.0'
+ dependencies:
+ typescript: 5.3.3
+ dev: true
+
+ /ts-dedent@2.2.0:
+ resolution: {integrity: sha512-q5W7tVM71e2xjHZTlgfTDoPF/SmqKG5hddq9SzR49CH2hayqRKJtQ4mtRlSxKaJlR/+9rEM+mnBHf7I2/BQcpQ==}
+ engines: {node: '>=6.10'}
+ dev: false
+
+ /ts-interface-checker@0.1.13:
+ resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==}
+
+ /tsconfig-paths@3.15.0:
+ resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==}
+ dependencies:
+ '@types/json5': 0.0.29
+ json5: 1.0.2
+ minimist: 1.2.8
+ strip-bom: 3.0.0
+ 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==}
+
+ /tsx@4.7.1:
+ resolution: {integrity: sha512-8d6VuibXHtlN5E3zFkgY8u4DX7Y3Z27zvvPKVmLon/D4AjuKzarkUBTLDBgj9iTQ0hg5xM7c/mYiRVM+HETf0g==}
+ engines: {node: '>=18.0.0'}
+ hasBin: true
+ dependencies:
+ esbuild: 0.19.12
+ get-tsconfig: 4.7.2
+ optionalDependencies:
+ fsevents: 2.3.3
+ dev: true
+
+ /tunnel-agent@0.6.0:
+ resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==}
+ dependencies:
+ safe-buffer: 5.2.1
+ dev: false
+
+ /type-check@0.4.0:
+ resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+ engines: {node: '>= 0.8.0'}
+ dependencies:
+ prelude-ls: 1.2.1
+ dev: true
+
+ /type-fest@0.16.0:
+ resolution: {integrity: sha512-eaBzG6MxNzEn9kiwvtre90cXaNLkmadMWa1zQMs3XORCXNbsH/OewwbxC5ia9dCxIxnTAsSxXJaa/p5y8DlvJg==}
+ engines: {node: '>=10'}
+
+ /type-fest@0.20.2:
+ resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
+ engines: {node: '>=10'}
+ dev: true
+
+ /type-fest@1.4.0:
+ resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==}
+ engines: {node: '>=10'}
+ dev: false
+
+ /typed-array-buffer@1.0.0:
+ resolution: {integrity: sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ call-bind: 1.0.5
+ get-intrinsic: 1.2.2
+ 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'}
+ dependencies:
+ call-bind: 1.0.5
+ 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'}
+ dependencies:
+ available-typed-arrays: 1.0.5
+ call-bind: 1.0.5
+ for-each: 0.3.3
+ 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==}
+ dependencies:
+ call-bind: 1.0.5
+ for-each: 0.3.3
+ is-typed-array: 1.1.12
+
+ /typescript@5.3.3:
+ resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ /unbox-primitive@1.0.2:
+ resolution: {integrity: sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==}
+ dependencies:
+ call-bind: 1.0.5
+ has-bigints: 1.0.2
+ has-symbols: 1.0.3
+ which-boxed-primitive: 1.0.2
+
+ /undici-types@5.26.5:
+ resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==}
+
+ /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'}
+ dependencies:
+ unicode-canonical-property-names-ecmascript: 2.0.0
+ unicode-property-aliases-ecmascript: 2.1.0
+
+ /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'}
+
+ /unified@10.1.2:
+ resolution: {integrity: sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q==}
+ dependencies:
+ '@types/unist': 2.0.10
+ bail: 2.0.2
+ extend: 3.0.2
+ is-buffer: 2.0.5
+ is-plain-obj: 4.1.0
+ trough: 2.2.0
+ vfile: 5.3.7
+ dev: false
+
+ /unique-string@2.0.0:
+ resolution: {integrity: sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg==}
+ engines: {node: '>=8'}
+ dependencies:
+ crypto-random-string: 2.0.0
+
+ /unist-util-find-after@5.0.0:
+ resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==}
+ dependencies:
+ '@types/unist': 3.0.2
+ unist-util-is: 6.0.0
+ dev: false
+
+ /unist-util-generated@2.0.1:
+ resolution: {integrity: sha512-qF72kLmPxAw0oN2fwpWIqbXAVyEqUzDHMsbtPvOudIlUzXYFIeQIuxXQCRCFh22B7cixvU0MG7m3MW8FTq/S+A==}
+ dev: false
+
+ /unist-util-is@5.2.1:
+ resolution: {integrity: sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw==}
+ dependencies:
+ '@types/unist': 2.0.10
+ dev: false
+
+ /unist-util-is@6.0.0:
+ resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==}
+ dependencies:
+ '@types/unist': 3.0.2
+ dev: false
+
+ /unist-util-position-from-estree@1.1.2:
+ resolution: {integrity: sha512-poZa0eXpS+/XpoQwGwl79UUdea4ol2ZuCYguVaJS4qzIOMDzbqz8a3erUCOmubSZkaOuGamb3tX790iwOIROww==}
+ dependencies:
+ '@types/unist': 2.0.10
+ dev: false
+
+ /unist-util-position@4.0.4:
+ resolution: {integrity: sha512-kUBE91efOWfIVBo8xzh/uZQ7p9ffYRtUbMRZBNFYwf0RK8koUMx6dGUfwylLOKmaT2cs4wSW96QoYUSXAyEtpg==}
+ dependencies:
+ '@types/unist': 2.0.10
+ dev: false
+
+ /unist-util-position@5.0.0:
+ resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==}
+ dependencies:
+ '@types/unist': 3.0.2
+ dev: false
+
+ /unist-util-remove-position@4.0.2:
+ resolution: {integrity: sha512-TkBb0HABNmxzAcfLf4qsIbFbaPDvMO6wa3b3j4VcEzFVaw1LBKwnW4/sRJ/atSLSzoIg41JWEdnE7N6DIhGDGQ==}
+ dependencies:
+ '@types/unist': 2.0.10
+ unist-util-visit: 4.1.2
+ dev: false
+
+ /unist-util-remove-position@5.0.0:
+ resolution: {integrity: sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q==}
+ dependencies:
+ '@types/unist': 3.0.2
+ unist-util-visit: 5.0.0
+ dev: false
+
+ /unist-util-remove@4.0.0:
+ resolution: {integrity: sha512-b4gokeGId57UVRX/eVKej5gXqGlc9+trkORhFJpu9raqZkZhU0zm8Doi05+HaiBsMEIJowL+2WtQ5ItjsngPXg==}
+ dependencies:
+ '@types/unist': 3.0.2
+ unist-util-is: 6.0.0
+ unist-util-visit-parents: 6.0.1
+ dev: false
+
+ /unist-util-stringify-position@3.0.3:
+ resolution: {integrity: sha512-k5GzIBZ/QatR8N5X2y+drfpWG8IDBzdnVj6OInRNWm1oXrzydiaAT2OQiA8DPRRZyAKb9b6I2a6PxYklZD0gKg==}
+ dependencies:
+ '@types/unist': 2.0.10
+ dev: false
+
+ /unist-util-stringify-position@4.0.0:
+ resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==}
+ dependencies:
+ '@types/unist': 3.0.2
+ dev: false
+
+ /unist-util-visit-parents@4.1.1:
+ resolution: {integrity: sha512-1xAFJXAKpnnJl8G7K5KgU7FY55y3GcLIXqkzUj5QF/QVP7biUm0K0O2oqVkYsdjzJKifYeWn9+o6piAK2hGSHw==}
+ dependencies:
+ '@types/unist': 2.0.10
+ unist-util-is: 5.2.1
+ dev: false
+
+ /unist-util-visit-parents@5.1.3:
+ resolution: {integrity: sha512-x6+y8g7wWMyQhL1iZfhIPhDAs7Xwbn9nRosDXl7qoPTSCy0yNxnKc+hWokFifWQIDGi154rdUqKvbCa4+1kLhg==}
+ dependencies:
+ '@types/unist': 2.0.10
+ unist-util-is: 5.2.1
+ dev: false
+
+ /unist-util-visit-parents@6.0.1:
+ resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==}
+ dependencies:
+ '@types/unist': 3.0.2
+ unist-util-is: 6.0.0
+ dev: false
+
+ /unist-util-visit@3.1.0:
+ resolution: {integrity: sha512-Szoh+R/Ll68QWAyQyZZpQzZQm2UPbxibDvaY8Xc9SUtYgPsDzx5AWSk++UUt2hJuow8mvwR+rG+LQLw+KsuAKA==}
+ dependencies:
+ '@types/unist': 2.0.10
+ unist-util-is: 5.2.1
+ unist-util-visit-parents: 4.1.1
+ dev: false
+
+ /unist-util-visit@4.1.2:
+ resolution: {integrity: sha512-MSd8OUGISqHdVvfY9TPhyK2VdUrPgxkUtWSuMHF6XAAFuL4LokseigBnZtPnJMu+FbynTkFNnFlyjxpVKujMRg==}
+ dependencies:
+ '@types/unist': 2.0.10
+ unist-util-is: 5.2.1
+ unist-util-visit-parents: 5.1.3
+ dev: false
+
+ /unist-util-visit@5.0.0:
+ resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==}
+ dependencies:
+ '@types/unist': 3.0.2
+ unist-util-is: 6.0.0
+ unist-util-visit-parents: 6.0.1
+ dev: false
+
+ /universalify@2.0.1:
+ resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
+ engines: {node: '>= 10.0.0'}
+
+ /upath@1.2.0:
+ resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==}
+ engines: {node: '>=4'}
+
+ /update-browserslist-db@1.0.13(browserslist@4.22.2):
+ resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+ dependencies:
+ browserslist: 4.22.2
+ escalade: 3.1.1
+ picocolors: 1.0.0
+
+ /update-browserslist-db@1.0.13(browserslist@4.22.3):
+ resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+ dependencies:
+ browserslist: 4.22.3
+ escalade: 3.1.1
+ picocolors: 1.0.0
+ dev: false
+
+ /uri-js@4.4.1:
+ resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+ dependencies:
+ punycode: 2.3.1
+
+ /use-callback-ref@1.3.1(@types/react@18.2.48)(react@18.2.0):
+ resolution: {integrity: sha512-Lg4Vx1XZQauB42Hw3kK7JM6yjVjgFmFC5/Ab797s79aARomD2nEErc4mCgM8EZrARLmmbWpi5DGCadmK50DcAQ==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@types/react': 18.2.48
+ react: 18.2.0
+ tslib: 2.6.2
+ dev: false
+
+ /use-sidecar@1.1.2(@types/react@18.2.48)(react@18.2.0):
+ resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@types/react': ^16.9.0 || ^17.0.0 || ^18.0.0
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@types/react': 18.2.48
+ detect-node-es: 1.1.0
+ react: 18.2.0
+ tslib: 2.6.2
+ dev: false
+
+ /use-sync-external-store@1.2.0(react@18.2.0):
+ resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==}
+ peerDependencies:
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0
+ dependencies:
+ react: 18.2.0
+ dev: false
+
+ /util-deprecate@1.0.2:
+ resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+ /uuid@8.3.2:
+ resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==}
+ hasBin: true
+ dev: false
+
+ /uuid@9.0.1:
+ resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
+ hasBin: true
+ dev: false
+
+ /uvu@0.5.6:
+ resolution: {integrity: sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA==}
+ engines: {node: '>=8'}
+ hasBin: true
+ dependencies:
+ dequal: 2.0.3
+ diff: 5.2.0
+ kleur: 4.1.5
+ sade: 1.8.1
+ dev: false
+
+ /vaul@0.8.9(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-gpmtmZRWDPP6niQh14JfRIFUYZVyfvAWyA/7rUINOfNlO/2K7uEvI5rLXEXkxZIRFyUZj+TPHLFMirkegPHjrw==}
+ peerDependencies:
+ react: ^16.8 || ^17.0 || ^18.0
+ react-dom: ^16.8 || ^17.0 || ^18.0
+ dependencies:
+ '@radix-ui/react-dialog': 1.0.5(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0)(react@18.2.0)
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ transitivePeerDependencies:
+ - '@types/react'
+ - '@types/react-dom'
+ dev: false
+
+ /vfile-location@5.0.2:
+ resolution: {integrity: sha512-NXPYyxyBSH7zB5U6+3uDdd6Nybz6o6/od9rk8bp9H8GR3L+cm/fC0uUTbqBmUTnMCUDslAGBOIKNfvvb+gGlDg==}
+ dependencies:
+ '@types/unist': 3.0.2
+ vfile: 6.0.1
+ dev: false
+
+ /vfile-matter@3.0.1:
+ resolution: {integrity: sha512-CAAIDwnh6ZdtrqAuxdElUqQRQDQgbbIrYtDYI8gCjXS1qQ+1XdLoK8FIZWxJwn0/I+BkSSZpar3SOgjemQz4fg==}
+ dependencies:
+ '@types/js-yaml': 4.0.9
+ is-buffer: 2.0.5
+ js-yaml: 4.1.0
+ dev: false
+
+ /vfile-message@3.1.4:
+ resolution: {integrity: sha512-fa0Z6P8HUrQN4BZaX05SIVXic+7kE3b05PWAtPuYP9QLHsLKYR7/AlLW3NtOrpXRLeawpDLMsVkmk5DG0NXgWw==}
+ dependencies:
+ '@types/unist': 2.0.10
+ unist-util-stringify-position: 3.0.3
+ dev: false
+
+ /vfile-message@4.0.2:
+ resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==}
+ dependencies:
+ '@types/unist': 3.0.2
+ unist-util-stringify-position: 4.0.0
+ dev: false
+
+ /vfile@5.3.7:
+ resolution: {integrity: sha512-r7qlzkgErKjobAmyNIkkSpizsFPYiUPuJb5pNW1RB4JcYVZhs4lIbVqk8XPk033CV/1z8ss5pkax8SuhGpcG8g==}
+ dependencies:
+ '@types/unist': 2.0.10
+ is-buffer: 2.0.5
+ unist-util-stringify-position: 3.0.3
+ vfile-message: 3.1.4
+ dev: false
+
+ /vfile@6.0.1:
+ resolution: {integrity: sha512-1bYqc7pt6NIADBJ98UiG0Bn/CHIVOoZ/IyEkqIruLg0mE1BKzkOXY2D6CSqQIcKqgadppE5lrxgWXJmXd7zZJw==}
+ dependencies:
+ '@types/unist': 3.0.2
+ unist-util-stringify-position: 4.0.0
+ vfile-message: 4.0.2
+ dev: false
+
+ /vscode-oniguruma@1.7.0:
+ resolution: {integrity: sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==}
+ dev: false
+
+ /vscode-textmate@8.0.0:
+ resolution: {integrity: sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg==}
+ dev: false
+
+ /watchpack@2.4.0:
+ resolution: {integrity: sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==}
+ engines: {node: '>=10.13.0'}
+ dependencies:
+ glob-to-regexp: 0.4.1
+ graceful-fs: 4.2.11
+
+ /web-namespaces@2.0.1:
+ resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
+ dev: false
+
+ /web-push@3.6.7:
+ resolution: {integrity: sha512-OpiIUe8cuGjrj3mMBFWY+e4MMIkW3SVT+7vEIjvD9kejGUypv8GPDf84JdPWskK8zMRIJ6xYGm+Kxr8YkPyA0A==}
+ engines: {node: '>= 16'}
+ hasBin: true
+ dependencies:
+ asn1.js: 5.4.1
+ http_ece: 1.2.0
+ https-proxy-agent: 7.0.4
+ jws: 4.0.0
+ minimist: 1.2.8
+ transitivePeerDependencies:
+ - supports-color
+ dev: false
+
+ /web-worker@1.3.0:
+ resolution: {integrity: sha512-BSR9wyRsy/KOValMgd5kMyr3JzpdeoR9KVId8u5GVlTTAtNChlsE4yTxeY7zMdNSyOmoKBv8NH2qeRY9Tg+IaA==}
+ dev: false
+
+ /webidl-conversions@4.0.2:
+ resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
+
+ /webpack-sources@1.4.3:
+ resolution: {integrity: sha512-lgTS3Xhv1lCOKo7SA5TjKXMjpSM4sBjNV5+q2bqesbSPs5FjGmU6jjtBSkX9b4qW87vDIsCIlUPOEhbZrMdjeQ==}
+ 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'}
+ dev: false
+
+ /webpack@5.90.1:
+ resolution: {integrity: sha512-SstPdlAC5IvgFnhiRok8hqJo/+ArAbNv7rhU4fnWGHNVfN59HSQFaxZDSAL3IFG2YmqxuRs+IU33milSxbPlog==}
+ engines: {node: '>=10.13.0'}
+ hasBin: true
+ peerDependencies:
+ webpack-cli: '*'
+ peerDependenciesMeta:
+ webpack-cli:
+ optional: true
+ dependencies:
+ '@types/eslint-scope': 3.7.7
+ '@types/estree': 1.0.5
+ '@webassemblyjs/ast': 1.11.6
+ '@webassemblyjs/wasm-edit': 1.11.6
+ '@webassemblyjs/wasm-parser': 1.11.6
+ acorn: 8.11.3
+ acorn-import-assertions: 1.9.0(acorn@8.11.3)
+ browserslist: 4.22.3
+ chrome-trace-event: 1.0.3
+ enhanced-resolve: 5.15.0
+ es-module-lexer: 1.4.1
+ 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
+ mime-types: 2.1.35
+ neo-async: 2.6.2
+ schema-utils: 3.3.0
+ tapable: 2.2.1
+ terser-webpack-plugin: 5.3.10(webpack@5.90.1)
+ watchpack: 2.4.0
+ webpack-sources: 3.2.3
+ transitivePeerDependencies:
+ - '@swc/core'
+ - esbuild
+ - uglify-js
+ dev: false
+
+ /whatwg-url@7.1.0:
+ resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==}
+ dependencies:
+ lodash.sortby: 4.7.0
+ tr46: 1.0.1
+ webidl-conversions: 4.0.2
+
+ /which-boxed-primitive@1.0.2:
+ resolution: {integrity: sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==}
+ dependencies:
+ is-bigint: 1.0.4
+ is-boolean-object: 1.1.2
+ is-number-object: 1.0.7
+ is-string: 1.0.7
+ is-symbol: 1.0.4
+
+ /which-builtin-type@1.1.3:
+ resolution: {integrity: sha512-YmjsSMDBYsM1CaFiayOVT06+KJeXf0o5M/CAd4o1lTadFAtacTUM49zoYxr/oroopFDfhvN6iEcBxUyc3gvKmw==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ function.prototype.name: 1.1.6
+ has-tostringtag: 1.0.0
+ is-async-function: 2.0.0
+ is-date-object: 1.0.5
+ is-finalizationregistry: 1.0.2
+ is-generator-function: 1.0.10
+ is-regex: 1.1.4
+ is-weakref: 1.0.2
+ isarray: 2.0.5
+ which-boxed-primitive: 1.0.2
+ which-collection: 1.0.1
+ which-typed-array: 1.1.13
+ dev: true
+
+ /which-collection@1.0.1:
+ resolution: {integrity: sha512-W8xeTUwaln8i3K/cY1nGXzdnVZlidBcagyNFtBdD5kxnb4TvGKR7FfSIS3mYpwWS1QUCutfKz8IY8RjftB0+1A==}
+ dependencies:
+ is-map: 2.0.2
+ is-set: 2.0.2
+ is-weakmap: 2.0.1
+ is-weakset: 2.0.2
+ dev: true
+
+ /which-typed-array@1.1.13:
+ resolution: {integrity: sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==}
+ engines: {node: '>= 0.4'}
+ dependencies:
+ available-typed-arrays: 1.0.5
+ call-bind: 1.0.5
+ for-each: 0.3.3
+ gopd: 1.0.1
+ has-tostringtag: 1.0.0
+
+ /which@1.3.1:
+ resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==}
+ hasBin: true
+ dependencies:
+ isexe: 2.0.0
+ dev: false
+
+ /which@2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+ dependencies:
+ isexe: 2.0.0
+
+ /workbox-background-sync@6.6.0:
+ resolution: {integrity: sha512-jkf4ZdgOJxC9u2vztxLuPT/UjlH7m/nWRQ/MgGL0v8BJHoZdVGJd18Kck+a0e55wGXdqyHO+4IQTk0685g4MUw==}
+ dependencies:
+ idb: 7.1.1
+ workbox-core: 6.6.0
+
+ /workbox-broadcast-update@6.6.0:
+ resolution: {integrity: sha512-nm+v6QmrIFaB/yokJmQ/93qIJ7n72NICxIwQwe5xsZiV2aI93MGGyEyzOzDPVz5THEr5rC3FJSsO3346cId64Q==}
+ dependencies:
+ workbox-core: 6.6.0
+
+ /workbox-build@6.6.0:
+ resolution: {integrity: sha512-Tjf+gBwOTuGyZwMz2Nk/B13Fuyeo0Q84W++bebbVsfr9iLkDSo6j6PST8tET9HYA58mlRXwlMGpyWO8ETJiXdQ==}
+ engines: {node: '>=10.0.0'}
+ dependencies:
+ '@apideck/better-ajv-errors': 0.3.6(ajv@8.12.0)
+ '@babel/core': 7.23.7
+ '@babel/preset-env': 7.23.8(@babel/core@7.23.7)
+ '@babel/runtime': 7.23.8
+ '@rollup/plugin-babel': 5.3.1(@babel/core@7.23.7)(rollup@2.79.1)
+ '@rollup/plugin-node-resolve': 11.2.1(rollup@2.79.1)
+ '@rollup/plugin-replace': 2.4.2(rollup@2.79.1)
+ '@surma/rollup-plugin-off-main-thread': 2.2.3
+ ajv: 8.12.0
+ common-tags: 1.8.2
+ fast-json-stable-stringify: 2.1.0
+ fs-extra: 9.1.0
+ glob: 7.2.3
+ lodash: 4.17.21
+ pretty-bytes: 5.6.0
+ rollup: 2.79.1
+ rollup-plugin-terser: 7.0.2(rollup@2.79.1)
+ source-map: 0.8.0-beta.0
+ stringify-object: 3.3.0
+ strip-comments: 2.0.1
+ tempy: 0.6.0
+ upath: 1.2.0
+ workbox-background-sync: 6.6.0
+ workbox-broadcast-update: 6.6.0
+ workbox-cacheable-response: 6.6.0
+ workbox-core: 6.6.0
+ workbox-expiration: 6.6.0
+ workbox-google-analytics: 6.6.0
+ workbox-navigation-preload: 6.6.0
+ workbox-precaching: 6.6.0
+ workbox-range-requests: 6.6.0
+ workbox-recipes: 6.6.0
+ workbox-routing: 6.6.0
+ workbox-strategies: 6.6.0
+ workbox-streams: 6.6.0
+ workbox-sw: 6.6.0
+ workbox-window: 6.6.0
+ transitivePeerDependencies:
+ - '@types/babel__core'
+ - supports-color
+
+ /workbox-cacheable-response@6.6.0:
+ resolution: {integrity: sha512-JfhJUSQDwsF1Xv3EV1vWzSsCOZn4mQ38bWEBR3LdvOxSPgB65gAM6cS2CX8rkkKHRgiLrN7Wxoyu+TuH67kHrw==}
+ deprecated: workbox-background-sync@6.6.0
+ dependencies:
+ workbox-core: 6.6.0
+
+ /workbox-core@6.6.0:
+ resolution: {integrity: sha512-GDtFRF7Yg3DD859PMbPAYPeJyg5gJYXuBQAC+wyrWuuXgpfoOrIQIvFRZnQ7+czTIQjIr1DhLEGFzZanAT/3bQ==}
+
+ /workbox-expiration@6.6.0:
+ resolution: {integrity: sha512-baplYXcDHbe8vAo7GYvyAmlS4f6998Jff513L4XvlzAOxcl8F620O91guoJ5EOf5qeXG4cGdNZHkkVAPouFCpw==}
+ dependencies:
+ idb: 7.1.1
+ workbox-core: 6.6.0
+
+ /workbox-google-analytics@6.6.0:
+ resolution: {integrity: sha512-p4DJa6OldXWd6M9zRl0H6vB9lkrmqYFkRQ2xEiNdBFp9U0LhsGO7hsBscVEyH9H2/3eZZt8c97NB2FD9U2NJ+Q==}
+ dependencies:
+ workbox-background-sync: 6.6.0
+ workbox-core: 6.6.0
+ workbox-routing: 6.6.0
+ workbox-strategies: 6.6.0
+
+ /workbox-navigation-preload@6.6.0:
+ resolution: {integrity: sha512-utNEWG+uOfXdaZmvhshrh7KzhDu/1iMHyQOV6Aqup8Mm78D286ugu5k9MFD9SzBT5TcwgwSORVvInaXWbvKz9Q==}
+ dependencies:
+ workbox-core: 6.6.0
+
+ /workbox-precaching@6.6.0:
+ resolution: {integrity: sha512-eYu/7MqtRZN1IDttl/UQcSZFkHP7dnvr/X3Vn6Iw6OsPMruQHiVjjomDFCNtd8k2RdjLs0xiz9nq+t3YVBcWPw==}
+ dependencies:
+ workbox-core: 6.6.0
+ workbox-routing: 6.6.0
+ workbox-strategies: 6.6.0
+
+ /workbox-range-requests@6.6.0:
+ resolution: {integrity: sha512-V3aICz5fLGq5DpSYEU8LxeXvsT//mRWzKrfBOIxzIdQnV/Wj7R+LyJVTczi4CQ4NwKhAaBVaSujI1cEjXW+hTw==}
+ dependencies:
+ workbox-core: 6.6.0
+
+ /workbox-recipes@6.6.0:
+ resolution: {integrity: sha512-TFi3kTgYw73t5tg73yPVqQC8QQjxJSeqjXRO4ouE/CeypmP2O/xqmB/ZFBBQazLTPxILUQ0b8aeh0IuxVn9a6A==}
+ dependencies:
+ workbox-cacheable-response: 6.6.0
+ workbox-core: 6.6.0
+ workbox-expiration: 6.6.0
+ workbox-precaching: 6.6.0
+ workbox-routing: 6.6.0
+ workbox-strategies: 6.6.0
+
+ /workbox-routing@6.6.0:
+ resolution: {integrity: sha512-x8gdN7VDBiLC03izAZRfU+WKUXJnbqt6PG9Uh0XuPRzJPpZGLKce/FkOX95dWHRpOHWLEq8RXzjW0O+POSkKvw==}
+ dependencies:
+ workbox-core: 6.6.0
+
+ /workbox-strategies@6.6.0:
+ resolution: {integrity: sha512-eC07XGuINAKUWDnZeIPdRdVja4JQtTuc35TZ8SwMb1ztjp7Ddq2CJ4yqLvWzFWGlYI7CG/YGqaETntTxBGdKgQ==}
+ dependencies:
+ workbox-core: 6.6.0
+
+ /workbox-streams@6.6.0:
+ resolution: {integrity: sha512-rfMJLVvwuED09CnH1RnIep7L9+mj4ufkTyDPVaXPKlhi9+0czCu+SJggWCIFbPpJaAZmp2iyVGLqS3RUmY3fxg==}
+ dependencies:
+ workbox-core: 6.6.0
+ workbox-routing: 6.6.0
+
+ /workbox-sw@6.6.0:
+ resolution: {integrity: sha512-R2IkwDokbtHUE4Kus8pKO5+VkPHD2oqTgl+XJwh4zbF1HyjAbgNmK/FneZHVU7p03XUt9ICfuGDYISWG9qV/CQ==}
+
+ /workbox-webpack-plugin@6.6.0(webpack@5.90.1):
+ resolution: {integrity: sha512-xNZIZHalboZU66Wa7x1YkjIqEy1gTR+zPM+kjrYJzqN7iurYZBctBLISyScjhkJKYuRrZUP0iqViZTh8rS0+3A==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ webpack: ^4.4.0 || ^5.9.0
+ dependencies:
+ fast-json-stable-stringify: 2.1.0
+ pretty-bytes: 5.6.0
+ upath: 1.2.0
+ webpack: 5.90.1
+ webpack-sources: 1.4.3
+ workbox-build: 6.6.0
+ transitivePeerDependencies:
+ - '@types/babel__core'
+ - supports-color
+ dev: false
+
+ /workbox-window@6.6.0:
+ resolution: {integrity: sha512-L4N9+vka17d16geaJXXRjENLFldvkWy7JyGxElRD0JvBxvFEd8LOhr+uXCcar/NzAmIBRv9EZ+M+Qr4mOoBITw==}
+ dependencies:
+ '@types/trusted-types': 2.0.7
+ workbox-core: 6.6.0
+
+ /wrap-ansi@7.0.0:
+ resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
+ engines: {node: '>=10'}
+ 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'}
+ dependencies:
+ ansi-styles: 6.2.1
+ string-width: 5.1.2
+ strip-ansi: 7.1.0
+
+ /wrappy@1.0.2:
+ resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+
+ /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@2.3.4:
+ resolution: {integrity: sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==}
+ engines: {node: '>= 14'}
+
+ /yocto-queue@0.1.0:
+ resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+ engines: {node: '>=10'}
+
+ /yocto-queue@1.0.0:
+ resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==}
+ engines: {node: '>=12.20'}
+ dev: false
+
+ /zod@3.22.4:
+ resolution: {integrity: sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==}
+ dev: false
+
+ /zustand@4.5.0(@types/react@18.2.48)(react@18.2.0):
+ resolution: {integrity: sha512-zlVFqS5TQ21nwijjhJlx4f9iGrXSL0o/+Dpy4txAP22miJ8Ti6c1Ol1RLNN98BMib83lmDH/2KmLwaNXpjrO1A==}
+ engines: {node: '>=12.7.0'}
+ peerDependencies:
+ '@types/react': '>=16.8'
+ immer: '>=9.0.6'
+ react: '>=16.8'
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ immer:
+ optional: true
+ react:
+ optional: true
+ dependencies:
+ '@types/react': 18.2.48
+ react: 18.2.0
+ use-sync-external-store: 1.2.0(react@18.2.0)
+ dev: false
+
+ /zwitch@2.0.4:
+ resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==}
+ dev: false
diff --git a/postcss.config.cjs b/postcss.config.cjs
new file mode 100644
index 0000000..1323aff
--- /dev/null
+++ b/postcss.config.cjs
@@ -0,0 +1,8 @@
+const config = {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+};
+
+module.exports = config;
diff --git a/prettier.config.js b/prettier.config.js
new file mode 100644
index 0000000..bcfffc9
--- /dev/null
+++ b/prettier.config.js
@@ -0,0 +1,10 @@
+/** @type {import('prettier').Config & import('prettier-plugin-tailwindcss').PluginOptions} */
+const config = {
+ plugins: ['prettier-plugin-tailwindcss'],
+ semi: true,
+ singleQuote: true,
+ trailingComma: 'all',
+ printWidth: 100,
+};
+
+export default config;
diff --git a/prisma/migrations/20240224010302_init/migration.sql b/prisma/migrations/20240224010302_init/migration.sql
new file mode 100644
index 0000000..0284742
--- /dev/null
+++ b/prisma/migrations/20240224010302_init/migration.sql
@@ -0,0 +1,208 @@
+-- CreateEnum
+CREATE TYPE "SplitType" AS ENUM ('EQUAL', 'PERCENTAGE', 'EXACT', 'SHARE', 'ADJUSTMENT', 'SETTLEMENT');
+
+-- CreateTable
+CREATE TABLE "Account" (
+ "id" TEXT NOT NULL,
+ "userId" INTEGER NOT NULL,
+ "type" TEXT NOT NULL,
+ "provider" TEXT NOT NULL,
+ "providerAccountId" TEXT NOT NULL,
+ "refresh_token" TEXT,
+ "access_token" TEXT,
+ "expires_at" INTEGER,
+ "token_type" TEXT,
+ "scope" TEXT,
+ "id_token" TEXT,
+ "session_state" TEXT,
+
+ CONSTRAINT "Account_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "Session" (
+ "id" TEXT NOT NULL,
+ "sessionToken" TEXT NOT NULL,
+ "userId" INTEGER NOT NULL,
+ "expires" TIMESTAMP(3) NOT NULL,
+
+ CONSTRAINT "Session_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "User" (
+ "id" SERIAL NOT NULL,
+ "name" TEXT,
+ "email" TEXT,
+ "emailVerified" TIMESTAMP(3),
+ "image" TEXT,
+ "currency" TEXT NOT NULL DEFAULT 'USD',
+
+ CONSTRAINT "User_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "VerificationToken" (
+ "identifier" TEXT NOT NULL,
+ "token" TEXT NOT NULL,
+ "expires" TIMESTAMP(3) NOT NULL
+);
+
+-- CreateTable
+CREATE TABLE "Balance" (
+ "userId" INTEGER NOT NULL,
+ "currency" TEXT NOT NULL,
+ "friendId" INTEGER NOT NULL,
+ "amount" INTEGER NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL,
+
+ CONSTRAINT "Balance_pkey" PRIMARY KEY ("userId","currency","friendId")
+);
+
+-- CreateTable
+CREATE TABLE "Group" (
+ "id" SERIAL NOT NULL,
+ "publicId" TEXT NOT NULL,
+ "name" TEXT NOT NULL,
+ "userId" INTEGER NOT NULL,
+ "defaultCurrency" TEXT NOT NULL DEFAULT 'USD',
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL,
+
+ CONSTRAINT "Group_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "GroupUser" (
+ "groupId" INTEGER NOT NULL,
+ "userId" INTEGER NOT NULL,
+
+ CONSTRAINT "GroupUser_pkey" PRIMARY KEY ("groupId","userId")
+);
+
+-- CreateTable
+CREATE TABLE "GroupBalance" (
+ "groupId" INTEGER NOT NULL,
+ "currency" TEXT NOT NULL,
+ "userId" INTEGER NOT NULL,
+ "firendId" INTEGER NOT NULL,
+ "amount" INTEGER NOT NULL,
+ "updatedAt" TIMESTAMP(3) NOT NULL,
+
+ CONSTRAINT "GroupBalance_pkey" PRIMARY KEY ("groupId","currency","firendId","userId")
+);
+
+-- CreateTable
+CREATE TABLE "Expense" (
+ "id" TEXT NOT NULL,
+ "paidBy" INTEGER NOT NULL,
+ "addedBy" INTEGER NOT NULL,
+ "name" TEXT NOT NULL,
+ "category" TEXT NOT NULL,
+ "amount" INTEGER NOT NULL,
+ "splitType" "SplitType" NOT NULL DEFAULT 'EQUAL',
+ "expenseDate" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ "updatedAt" TIMESTAMP(3) NOT NULL,
+ "currency" TEXT NOT NULL,
+ "fileKey" TEXT,
+ "groupId" INTEGER,
+
+ CONSTRAINT "Expense_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateTable
+CREATE TABLE "ExpenseParticipant" (
+ "expenseId" TEXT NOT NULL,
+ "userId" INTEGER NOT NULL,
+ "amount" INTEGER NOT NULL,
+
+ CONSTRAINT "ExpenseParticipant_pkey" PRIMARY KEY ("expenseId","userId")
+);
+
+-- CreateTable
+CREATE TABLE "ExpenseNote" (
+ "id" TEXT NOT NULL,
+ "expenseId" TEXT NOT NULL,
+ "note" TEXT NOT NULL,
+ "createdById" INTEGER NOT NULL,
+ "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
+
+ CONSTRAINT "ExpenseNote_pkey" PRIMARY KEY ("id")
+);
+
+-- CreateIndex
+CREATE UNIQUE INDEX "Account_provider_providerAccountId_key" ON "Account"("provider", "providerAccountId");
+
+-- CreateIndex
+CREATE UNIQUE INDEX "Session_sessionToken_key" ON "Session"("sessionToken");
+
+-- CreateIndex
+CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
+
+-- CreateIndex
+CREATE UNIQUE INDEX "VerificationToken_token_key" ON "VerificationToken"("token");
+
+-- CreateIndex
+CREATE UNIQUE INDEX "VerificationToken_identifier_token_key" ON "VerificationToken"("identifier", "token");
+
+-- CreateIndex
+CREATE UNIQUE INDEX "Group_publicId_key" ON "Group"("publicId");
+
+-- CreateIndex
+CREATE INDEX "Expense_groupId_idx" ON "Expense"("groupId");
+
+-- CreateIndex
+CREATE INDEX "Expense_paidBy_idx" ON "Expense"("paidBy");
+
+-- AddForeignKey
+ALTER TABLE "Account" ADD CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "Balance" ADD CONSTRAINT "Balance_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "Balance" ADD CONSTRAINT "Balance_friendId_fkey" FOREIGN KEY ("friendId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "Group" ADD CONSTRAINT "Group_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "GroupUser" ADD CONSTRAINT "GroupUser_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "GroupUser" ADD CONSTRAINT "GroupUser_groupId_fkey" FOREIGN KEY ("groupId") REFERENCES "Group"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "GroupBalance" ADD CONSTRAINT "GroupBalance_groupId_fkey" FOREIGN KEY ("groupId") REFERENCES "Group"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "GroupBalance" ADD CONSTRAINT "GroupBalance_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "GroupBalance" ADD CONSTRAINT "GroupBalance_firendId_fkey" FOREIGN KEY ("firendId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "Expense" ADD CONSTRAINT "Expense_groupId_fkey" FOREIGN KEY ("groupId") REFERENCES "Group"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "Expense" ADD CONSTRAINT "Expense_paidBy_fkey" FOREIGN KEY ("paidBy") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "Expense" ADD CONSTRAINT "Expense_addedBy_fkey" FOREIGN KEY ("addedBy") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "ExpenseParticipant" ADD CONSTRAINT "ExpenseParticipant_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "ExpenseParticipant" ADD CONSTRAINT "ExpenseParticipant_expenseId_fkey" FOREIGN KEY ("expenseId") REFERENCES "Expense"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "ExpenseNote" ADD CONSTRAINT "ExpenseNote_createdById_fkey" FOREIGN KEY ("createdById") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
+
+-- AddForeignKey
+ALTER TABLE "ExpenseNote" ADD CONSTRAINT "ExpenseNote_expenseId_fkey" FOREIGN KEY ("expenseId") REFERENCES "Expense"("id") ON DELETE CASCADE ON UPDATE CASCADE;
diff --git a/prisma/migrations/20240228204411_add_deleted_at_for_expense/migration.sql b/prisma/migrations/20240228204411_add_deleted_at_for_expense/migration.sql
new file mode 100644
index 0000000..5731e38
--- /dev/null
+++ b/prisma/migrations/20240228204411_add_deleted_at_for_expense/migration.sql
@@ -0,0 +1,6 @@
+-- AlterTable
+ALTER TABLE "Expense" ADD COLUMN "deletedAt" TIMESTAMP(3),
+ADD COLUMN "deletedBy" INTEGER;
+
+-- AddForeignKey
+ALTER TABLE "Expense" ADD CONSTRAINT "Expense_deletedBy_fkey" FOREIGN KEY ("deletedBy") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
diff --git a/prisma/migrations/20240311110627_add_push_notification/migration.sql b/prisma/migrations/20240311110627_add_push_notification/migration.sql
new file mode 100644
index 0000000..51aa34e
--- /dev/null
+++ b/prisma/migrations/20240311110627_add_push_notification/migration.sql
@@ -0,0 +1,7 @@
+-- CreateTable
+CREATE TABLE "PushNotification" (
+ "userId" INTEGER NOT NULL,
+ "subscription" TEXT NOT NULL,
+
+ CONSTRAINT "PushNotification_pkey" PRIMARY KEY ("userId")
+);
diff --git a/prisma/migrations/20240330050427_add_imported_from_splitiwise_user/migration.sql b/prisma/migrations/20240330050427_add_imported_from_splitiwise_user/migration.sql
new file mode 100644
index 0000000..0fba06c
--- /dev/null
+++ b/prisma/migrations/20240330050427_add_imported_from_splitiwise_user/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE "Balance" ADD COLUMN "importedFromSplitwise" BOOLEAN NOT NULL DEFAULT false;
diff --git a/prisma/migrations/20240330061939_add_splitwise_group_id/migration.sql b/prisma/migrations/20240330061939_add_splitwise_group_id/migration.sql
new file mode 100644
index 0000000..7bb23d6
--- /dev/null
+++ b/prisma/migrations/20240330061939_add_splitwise_group_id/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE "Group" ADD COLUMN "splitwiseGroupId" TEXT;
diff --git a/prisma/migrations/20240330064009_add_splitwise_group_id_unique/migration.sql b/prisma/migrations/20240330064009_add_splitwise_group_id_unique/migration.sql
new file mode 100644
index 0000000..ba84afe
--- /dev/null
+++ b/prisma/migrations/20240330064009_add_splitwise_group_id_unique/migration.sql
@@ -0,0 +1,8 @@
+/*
+ Warnings:
+
+ - A unique constraint covering the columns `[splitwiseGroupId]` on the table `Group` will be added. If there are existing duplicate values, this will fail.
+
+*/
+-- CreateIndex
+CREATE UNIQUE INDEX "Group_splitwiseGroupId_key" ON "Group"("splitwiseGroupId");
diff --git a/prisma/migrations/20241116203000_add_updated_by_for_expense/migration.sql b/prisma/migrations/20241116203000_add_updated_by_for_expense/migration.sql
new file mode 100644
index 0000000..76f355d
--- /dev/null
+++ b/prisma/migrations/20241116203000_add_updated_by_for_expense/migration.sql
@@ -0,0 +1,5 @@
+-- AlterTable
+ALTER TABLE "Expense" ADD COLUMN "updatedBy" INTEGER;
+
+-- AddForeignKey
+ALTER TABLE "Expense" ADD CONSTRAINT "Expense_updatedBy_fkey" FOREIGN KEY ("updatedBy") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml
new file mode 100644
index 0000000..6bf9015
--- /dev/null
+++ b/prisma/migrations/migration_lock.toml
@@ -0,0 +1,3 @@
+# Please do not edit this file manually
+# It should be added in your version-control system (i.e. Git)
+provider = "postgresql"
\ No newline at end of file
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
new file mode 100644
index 0000000..51c9df7
--- /dev/null
+++ b/prisma/schema.prisma
@@ -0,0 +1,189 @@
+// This is your Prisma schema file,
+// learn more about it in the docs: https://pris.ly/d/prisma-schema
+
+generator client {
+ provider = "prisma-client-js"
+ previewFeatures = ["relationJoins"]
+}
+
+datasource db {
+ provider = "postgresql"
+ // NOTE: When using mysql or sqlserver, uncomment the @db.Text annotations in model Account below
+ // Further reading:
+ // https://next-auth.js.org/adapters/prisma#create-the-prisma-schema
+ // https://www.prisma.io/docs/reference/api-reference/prisma-schema-reference#string
+ url = env("DATABASE_URL")
+}
+
+// Necessary for Next auth
+model Account {
+ id String @id @default(cuid())
+ userId Int
+ type String
+ provider String
+ providerAccountId String
+ refresh_token String? // @db.Text
+ access_token String? // @db.Text
+ expires_at Int?
+ token_type String?
+ scope String?
+ id_token String? // @db.Text
+ session_state String?
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
+
+ @@unique([provider, providerAccountId])
+}
+
+model Session {
+ id String @id @default(cuid())
+ sessionToken String @unique
+ userId Int
+ expires DateTime
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
+}
+
+model User {
+ id Int @id @default(autoincrement())
+ name String?
+ email String? @unique
+ emailVerified DateTime?
+ image String?
+ currency String @default("USD")
+ accounts Account[]
+ sessions Session[]
+ groups Group[]
+ associatedGroups GroupUser[]
+ expenseParticipants ExpenseParticipant[]
+ expenseNotes ExpenseNote[]
+ userBalances Balance[] @relation("UserBalance")
+ friendBalances Balance[] @relation("FriendBalance")
+ groupUserBalances GroupBalance[] @relation("GroupUserBalance")
+ groupFriendBalances GroupBalance[] @relation("GroupFriendBalance")
+ paidExpenses Expense[] @relation("PaidByUser")
+ addedExpenses Expense[] @relation("AddedByUser")
+ deletedExpenses Expense[] @relation("DeletedByUser")
+ updatedExpenses Expense[] @relation("UpdatedByUser")
+}
+
+model VerificationToken {
+ identifier String
+ token String @unique
+ expires DateTime
+
+ @@unique([identifier, token])
+}
+
+model Balance {
+ userId Int
+ currency String
+ friendId Int
+ amount Int
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ importedFromSplitwise Boolean @default(false)
+ user User @relation(name: "UserBalance", fields: [userId], references: [id], onDelete: Cascade)
+ friend User @relation(name: "FriendBalance", fields: [friendId], references: [id], onDelete: Cascade)
+
+ @@id([userId, currency, friendId])
+}
+
+model Group {
+ id Int @id @default(autoincrement())
+ publicId String @unique
+ name String
+ userId Int
+ defaultCurrency String @default("USD")
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ splitwiseGroupId String? @unique
+ createdBy User @relation(fields: [userId], references: [id], onDelete: Cascade)
+ groupUsers GroupUser[]
+ expenses Expense[]
+ groupBalances GroupBalance[]
+}
+
+model GroupUser {
+ groupId Int
+ userId Int
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
+ group Group @relation(fields: [groupId], references: [id], onDelete: Cascade)
+
+ @@id([groupId, userId])
+}
+
+model GroupBalance {
+ groupId Int
+ currency String
+ userId Int
+ firendId Int
+ amount Int
+ updatedAt DateTime @updatedAt
+ group Group @relation(fields: [groupId], references: [id], onDelete: Cascade)
+ user User @relation(name: "GroupUserBalance", fields: [userId], references: [id], onDelete: Cascade)
+ friend User @relation(name: "GroupFriendBalance", fields: [firendId], references: [id], onDelete: Cascade)
+
+ @@id([groupId, currency, firendId, userId])
+}
+
+enum SplitType {
+ EQUAL
+ PERCENTAGE
+ EXACT
+ SHARE
+ ADJUSTMENT
+ SETTLEMENT
+}
+
+model Expense {
+ id String @id @default(cuid())
+ paidBy Int
+ addedBy Int
+ name String
+ category String
+ amount Int
+ splitType SplitType @default(EQUAL)
+ expenseDate DateTime @default(now())
+ createdAt DateTime @default(now())
+ updatedAt DateTime @updatedAt
+ currency String
+ fileKey String?
+ groupId Int?
+ deletedAt DateTime?
+ deletedBy Int?
+ updatedBy Int?
+ group Group? @relation(fields: [groupId], references: [id], onDelete: Cascade)
+ paidByUser User @relation(name: "PaidByUser", fields: [paidBy], references: [id], onDelete: Cascade)
+ addedByUser User @relation(name: "AddedByUser", fields: [addedBy], references: [id], onDelete: Cascade)
+ deletedByUser User? @relation(name: "DeletedByUser", fields: [deletedBy], references: [id], onDelete: Cascade)
+ updatedByUser User? @relation(name: "UpdatedByUser", fields: [updatedBy], references: [id], onDelete: SetNull)
+ expenseParticipants ExpenseParticipant[]
+ expenseNotes ExpenseNote[]
+
+ @@index([groupId])
+ @@index([paidBy])
+}
+
+model ExpenseParticipant {
+ expenseId String
+ userId Int
+ amount Int
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
+ expense Expense @relation(fields: [expenseId], references: [id], onDelete: Cascade)
+
+ @@id([expenseId, userId])
+}
+
+model ExpenseNote {
+ id String @id @default(cuid())
+ expenseId String
+ note String
+ createdById Int
+ createdAt DateTime @default(now())
+ createdBy User @relation(fields: [createdById], references: [id], onDelete: Cascade)
+ expense Expense @relation(fields: [expenseId], references: [id], onDelete: Cascade)
+}
+
+model PushNotification {
+ userId Int @id
+ subscription String
+}
diff --git a/prisma/seed.ts b/prisma/seed.ts
new file mode 100644
index 0000000..3cd2341
--- /dev/null
+++ b/prisma/seed.ts
@@ -0,0 +1,73 @@
+import { PrismaClient } from '@prisma/client';
+
+const prisma = new PrismaClient();
+
+async function createUsers() {
+ const users = await prisma.user.createMany({
+ data: [
+ {
+ name: 'Alice',
+ email: 'alice@example.com',
+ currency: 'USD',
+ },
+ {
+ name: 'Bob',
+ email: 'bob@example.com',
+ currency: 'EUR',
+ },
+ {
+ name: 'Charlie',
+ email: 'charlie@example.com',
+ currency: 'GBP',
+ },
+ {
+ name: 'Diana',
+ email: 'diana@example.com',
+ currency: 'JPY',
+ },
+ {
+ name: 'Evan',
+ email: 'evan@example.com',
+ currency: 'CNY',
+ },
+ ],
+ });
+
+ return prisma.user.findMany();
+}
+
+async function createGroups() {
+ // Assuming Alice creates a group and adds Bob and Charlie
+
+ const users = await prisma.user.findMany();
+
+ if (users.length) {
+ const group = await prisma.group.create({
+ data: {
+ name: 'Holiday Trip',
+ publicId: 'holiday-trip-123',
+ defaultCurrency: 'USD',
+ createdBy: { connect: { id: users[0]?.id } },
+ },
+ });
+
+ await prisma.groupUser.createMany({
+ data: users.map((u) => ({ groupId: group.id, userId: u.id })),
+ });
+ console.log('Group created and users added');
+ }
+}
+
+async function main() {
+ await createUsers();
+ await createGroups();
+}
+
+main()
+ .catch((e) => {
+ console.error(e);
+ process.exit(1);
+ })
+ .finally(() => {
+ prisma.$disconnect().catch(console.log);
+ });
diff --git a/public/Desktop.webp b/public/Desktop.webp
new file mode 100644
index 0000000..9671f1d
Binary files /dev/null and b/public/Desktop.webp differ
diff --git a/public/add_expense.svg b/public/add_expense.svg
new file mode 100644
index 0000000..422f5b1
--- /dev/null
+++ b/public/add_expense.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/empty_img.svg b/public/empty_img.svg
new file mode 100644
index 0000000..4f1af3c
--- /dev/null
+++ b/public/empty_img.svg
@@ -0,0 +1 @@
+moonlight
\ No newline at end of file
diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 0000000..38c3b73
Binary files /dev/null and b/public/favicon.ico differ
diff --git a/public/group.svg b/public/group.svg
new file mode 100644
index 0000000..72c2e97
--- /dev/null
+++ b/public/group.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/hero.webp b/public/hero.webp
new file mode 100644
index 0000000..f21159a
Binary files /dev/null and b/public/hero.webp differ
diff --git a/public/icons/android-chrome-192x192.png b/public/icons/android-chrome-192x192.png
new file mode 100644
index 0000000..8569ad8
Binary files /dev/null and b/public/icons/android-chrome-192x192.png differ
diff --git a/public/icons/android-chrome-512x512.png b/public/icons/android-chrome-512x512.png
new file mode 100644
index 0000000..68321d8
Binary files /dev/null and b/public/icons/android-chrome-512x512.png differ
diff --git a/public/icons/android/android-launchericon-144-144.png b/public/icons/android/android-launchericon-144-144.png
new file mode 100644
index 0000000..fe2f213
Binary files /dev/null and b/public/icons/android/android-launchericon-144-144.png differ
diff --git a/public/icons/android/android-launchericon-192-192.png b/public/icons/android/android-launchericon-192-192.png
new file mode 100644
index 0000000..36cfea4
Binary files /dev/null and b/public/icons/android/android-launchericon-192-192.png differ
diff --git a/public/icons/android/android-launchericon-48-48.png b/public/icons/android/android-launchericon-48-48.png
new file mode 100644
index 0000000..2ce1505
Binary files /dev/null and b/public/icons/android/android-launchericon-48-48.png differ
diff --git a/public/icons/android/android-launchericon-512-512.png b/public/icons/android/android-launchericon-512-512.png
new file mode 100644
index 0000000..a66d3cc
Binary files /dev/null and b/public/icons/android/android-launchericon-512-512.png differ
diff --git a/public/icons/android/android-launchericon-72-72.png b/public/icons/android/android-launchericon-72-72.png
new file mode 100644
index 0000000..4032614
Binary files /dev/null and b/public/icons/android/android-launchericon-72-72.png differ
diff --git a/public/icons/android/android-launchericon-96-96.png b/public/icons/android/android-launchericon-96-96.png
new file mode 100644
index 0000000..2db923c
Binary files /dev/null and b/public/icons/android/android-launchericon-96-96.png differ
diff --git a/public/icons/apple-touch-icon.png b/public/icons/apple-touch-icon.png
new file mode 100644
index 0000000..9afbcae
Binary files /dev/null and b/public/icons/apple-touch-icon.png differ
diff --git a/public/icons/favicon-16x16.png b/public/icons/favicon-16x16.png
new file mode 100644
index 0000000..736787a
Binary files /dev/null and b/public/icons/favicon-16x16.png differ
diff --git a/public/icons/favicon-32x32.png b/public/icons/favicon-32x32.png
new file mode 100644
index 0000000..294aee2
Binary files /dev/null and b/public/icons/favicon-32x32.png differ
diff --git a/public/icons/icons.json b/public/icons/icons.json
new file mode 100644
index 0000000..9d9dbbf
--- /dev/null
+++ b/public/icons/icons.json
@@ -0,0 +1,452 @@
+{
+ "icons": [
+ {
+ "src": "windows11/SmallTile.scale-100.png",
+ "sizes": "71x71"
+ },
+ {
+ "src": "windows11/SmallTile.scale-125.png",
+ "sizes": "89x89"
+ },
+ {
+ "src": "windows11/SmallTile.scale-150.png",
+ "sizes": "107x107"
+ },
+ {
+ "src": "windows11/SmallTile.scale-200.png",
+ "sizes": "142x142"
+ },
+ {
+ "src": "windows11/SmallTile.scale-400.png",
+ "sizes": "284x284"
+ },
+ {
+ "src": "windows11/Square150x150Logo.scale-100.png",
+ "sizes": "150x150"
+ },
+ {
+ "src": "windows11/Square150x150Logo.scale-125.png",
+ "sizes": "188x188"
+ },
+ {
+ "src": "windows11/Square150x150Logo.scale-150.png",
+ "sizes": "225x225"
+ },
+ {
+ "src": "windows11/Square150x150Logo.scale-200.png",
+ "sizes": "300x300"
+ },
+ {
+ "src": "windows11/Square150x150Logo.scale-400.png",
+ "sizes": "600x600"
+ },
+ {
+ "src": "windows11/Wide310x150Logo.scale-100.png",
+ "sizes": "310x150"
+ },
+ {
+ "src": "windows11/Wide310x150Logo.scale-125.png",
+ "sizes": "388x188"
+ },
+ {
+ "src": "windows11/Wide310x150Logo.scale-150.png",
+ "sizes": "465x225"
+ },
+ {
+ "src": "windows11/Wide310x150Logo.scale-200.png",
+ "sizes": "620x300"
+ },
+ {
+ "src": "windows11/Wide310x150Logo.scale-400.png",
+ "sizes": "1240x600"
+ },
+ {
+ "src": "windows11/LargeTile.scale-100.png",
+ "sizes": "310x310"
+ },
+ {
+ "src": "windows11/LargeTile.scale-125.png",
+ "sizes": "388x388"
+ },
+ {
+ "src": "windows11/LargeTile.scale-150.png",
+ "sizes": "465x465"
+ },
+ {
+ "src": "windows11/LargeTile.scale-200.png",
+ "sizes": "620x620"
+ },
+ {
+ "src": "windows11/LargeTile.scale-400.png",
+ "sizes": "1240x1240"
+ },
+ {
+ "src": "windows11/Square44x44Logo.scale-100.png",
+ "sizes": "44x44"
+ },
+ {
+ "src": "windows11/Square44x44Logo.scale-125.png",
+ "sizes": "55x55"
+ },
+ {
+ "src": "windows11/Square44x44Logo.scale-150.png",
+ "sizes": "66x66"
+ },
+ {
+ "src": "windows11/Square44x44Logo.scale-200.png",
+ "sizes": "88x88"
+ },
+ {
+ "src": "windows11/Square44x44Logo.scale-400.png",
+ "sizes": "176x176"
+ },
+ {
+ "src": "windows11/StoreLogo.scale-100.png",
+ "sizes": "50x50"
+ },
+ {
+ "src": "windows11/StoreLogo.scale-125.png",
+ "sizes": "63x63"
+ },
+ {
+ "src": "windows11/StoreLogo.scale-150.png",
+ "sizes": "75x75"
+ },
+ {
+ "src": "windows11/StoreLogo.scale-200.png",
+ "sizes": "100x100"
+ },
+ {
+ "src": "windows11/StoreLogo.scale-400.png",
+ "sizes": "200x200"
+ },
+ {
+ "src": "windows11/SplashScreen.scale-100.png",
+ "sizes": "620x300"
+ },
+ {
+ "src": "windows11/SplashScreen.scale-125.png",
+ "sizes": "775x375"
+ },
+ {
+ "src": "windows11/SplashScreen.scale-150.png",
+ "sizes": "930x450"
+ },
+ {
+ "src": "windows11/SplashScreen.scale-200.png",
+ "sizes": "1240x600"
+ },
+ {
+ "src": "windows11/SplashScreen.scale-400.png",
+ "sizes": "2480x1200"
+ },
+ {
+ "src": "windows11/Square44x44Logo.targetsize-16.png",
+ "sizes": "16x16"
+ },
+ {
+ "src": "windows11/Square44x44Logo.targetsize-20.png",
+ "sizes": "20x20"
+ },
+ {
+ "src": "windows11/Square44x44Logo.targetsize-24.png",
+ "sizes": "24x24"
+ },
+ {
+ "src": "windows11/Square44x44Logo.targetsize-30.png",
+ "sizes": "30x30"
+ },
+ {
+ "src": "windows11/Square44x44Logo.targetsize-32.png",
+ "sizes": "32x32"
+ },
+ {
+ "src": "windows11/Square44x44Logo.targetsize-36.png",
+ "sizes": "36x36"
+ },
+ {
+ "src": "windows11/Square44x44Logo.targetsize-40.png",
+ "sizes": "40x40"
+ },
+ {
+ "src": "windows11/Square44x44Logo.targetsize-44.png",
+ "sizes": "44x44"
+ },
+ {
+ "src": "windows11/Square44x44Logo.targetsize-48.png",
+ "sizes": "48x48"
+ },
+ {
+ "src": "windows11/Square44x44Logo.targetsize-60.png",
+ "sizes": "60x60"
+ },
+ {
+ "src": "windows11/Square44x44Logo.targetsize-64.png",
+ "sizes": "64x64"
+ },
+ {
+ "src": "windows11/Square44x44Logo.targetsize-72.png",
+ "sizes": "72x72"
+ },
+ {
+ "src": "windows11/Square44x44Logo.targetsize-80.png",
+ "sizes": "80x80"
+ },
+ {
+ "src": "windows11/Square44x44Logo.targetsize-96.png",
+ "sizes": "96x96"
+ },
+ {
+ "src": "windows11/Square44x44Logo.targetsize-256.png",
+ "sizes": "256x256"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-unplated_targetsize-16.png",
+ "sizes": "16x16"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-unplated_targetsize-20.png",
+ "sizes": "20x20"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-unplated_targetsize-24.png",
+ "sizes": "24x24"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-unplated_targetsize-30.png",
+ "sizes": "30x30"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-unplated_targetsize-32.png",
+ "sizes": "32x32"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-unplated_targetsize-36.png",
+ "sizes": "36x36"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-unplated_targetsize-40.png",
+ "sizes": "40x40"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-unplated_targetsize-44.png",
+ "sizes": "44x44"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-unplated_targetsize-48.png",
+ "sizes": "48x48"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-unplated_targetsize-60.png",
+ "sizes": "60x60"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-unplated_targetsize-64.png",
+ "sizes": "64x64"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-unplated_targetsize-72.png",
+ "sizes": "72x72"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-unplated_targetsize-80.png",
+ "sizes": "80x80"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-unplated_targetsize-96.png",
+ "sizes": "96x96"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-unplated_targetsize-256.png",
+ "sizes": "256x256"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-lightunplated_targetsize-16.png",
+ "sizes": "16x16"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-lightunplated_targetsize-20.png",
+ "sizes": "20x20"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-lightunplated_targetsize-24.png",
+ "sizes": "24x24"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-lightunplated_targetsize-30.png",
+ "sizes": "30x30"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-lightunplated_targetsize-32.png",
+ "sizes": "32x32"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-lightunplated_targetsize-36.png",
+ "sizes": "36x36"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-lightunplated_targetsize-40.png",
+ "sizes": "40x40"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-lightunplated_targetsize-44.png",
+ "sizes": "44x44"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-lightunplated_targetsize-48.png",
+ "sizes": "48x48"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-lightunplated_targetsize-60.png",
+ "sizes": "60x60"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-lightunplated_targetsize-64.png",
+ "sizes": "64x64"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-lightunplated_targetsize-72.png",
+ "sizes": "72x72"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-lightunplated_targetsize-80.png",
+ "sizes": "80x80"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-lightunplated_targetsize-96.png",
+ "sizes": "96x96"
+ },
+ {
+ "src": "windows11/Square44x44Logo.altform-lightunplated_targetsize-256.png",
+ "sizes": "256x256"
+ },
+ {
+ "src": "android/android-launchericon-512-512.png",
+ "sizes": "512x512"
+ },
+ {
+ "src": "android/android-launchericon-192-192.png",
+ "sizes": "192x192"
+ },
+ {
+ "src": "android/android-launchericon-144-144.png",
+ "sizes": "144x144"
+ },
+ {
+ "src": "android/android-launchericon-96-96.png",
+ "sizes": "96x96"
+ },
+ {
+ "src": "android/android-launchericon-72-72.png",
+ "sizes": "72x72"
+ },
+ {
+ "src": "android/android-launchericon-48-48.png",
+ "sizes": "48x48"
+ },
+ {
+ "src": "ios/16.png",
+ "sizes": "16x16"
+ },
+ {
+ "src": "ios/20.png",
+ "sizes": "20x20"
+ },
+ {
+ "src": "ios/29.png",
+ "sizes": "29x29"
+ },
+ {
+ "src": "ios/32.png",
+ "sizes": "32x32"
+ },
+ {
+ "src": "ios/40.png",
+ "sizes": "40x40"
+ },
+ {
+ "src": "ios/50.png",
+ "sizes": "50x50"
+ },
+ {
+ "src": "ios/57.png",
+ "sizes": "57x57"
+ },
+ {
+ "src": "ios/58.png",
+ "sizes": "58x58"
+ },
+ {
+ "src": "ios/60.png",
+ "sizes": "60x60"
+ },
+ {
+ "src": "ios/64.png",
+ "sizes": "64x64"
+ },
+ {
+ "src": "ios/72.png",
+ "sizes": "72x72"
+ },
+ {
+ "src": "ios/76.png",
+ "sizes": "76x76"
+ },
+ {
+ "src": "ios/80.png",
+ "sizes": "80x80"
+ },
+ {
+ "src": "ios/87.png",
+ "sizes": "87x87"
+ },
+ {
+ "src": "ios/100.png",
+ "sizes": "100x100"
+ },
+ {
+ "src": "ios/114.png",
+ "sizes": "114x114"
+ },
+ {
+ "src": "ios/120.png",
+ "sizes": "120x120"
+ },
+ {
+ "src": "ios/128.png",
+ "sizes": "128x128"
+ },
+ {
+ "src": "ios/144.png",
+ "sizes": "144x144"
+ },
+ {
+ "src": "ios/152.png",
+ "sizes": "152x152"
+ },
+ {
+ "src": "ios/167.png",
+ "sizes": "167x167"
+ },
+ {
+ "src": "ios/180.png",
+ "sizes": "180x180"
+ },
+ {
+ "src": "ios/192.png",
+ "sizes": "192x192"
+ },
+ {
+ "src": "ios/256.png",
+ "sizes": "256x256"
+ },
+ {
+ "src": "ios/512.png",
+ "sizes": "512x512"
+ },
+ {
+ "src": "ios/1024.png",
+ "sizes": "1024x1024"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/public/icons/ios/100.png b/public/icons/ios/100.png
new file mode 100644
index 0000000..5249699
Binary files /dev/null and b/public/icons/ios/100.png differ
diff --git a/public/icons/ios/1024.png b/public/icons/ios/1024.png
new file mode 100644
index 0000000..280d2f5
Binary files /dev/null and b/public/icons/ios/1024.png differ
diff --git a/public/icons/ios/114.png b/public/icons/ios/114.png
new file mode 100644
index 0000000..2246ce6
Binary files /dev/null and b/public/icons/ios/114.png differ
diff --git a/public/icons/ios/120.png b/public/icons/ios/120.png
new file mode 100644
index 0000000..6cfee49
Binary files /dev/null and b/public/icons/ios/120.png differ
diff --git a/public/icons/ios/128.png b/public/icons/ios/128.png
new file mode 100644
index 0000000..c9fe903
Binary files /dev/null and b/public/icons/ios/128.png differ
diff --git a/public/icons/ios/144.png b/public/icons/ios/144.png
new file mode 100644
index 0000000..fe2f213
Binary files /dev/null and b/public/icons/ios/144.png differ
diff --git a/public/icons/ios/152.png b/public/icons/ios/152.png
new file mode 100644
index 0000000..d201988
Binary files /dev/null and b/public/icons/ios/152.png differ
diff --git a/public/icons/ios/16.png b/public/icons/ios/16.png
new file mode 100644
index 0000000..11a9a18
Binary files /dev/null and b/public/icons/ios/16.png differ
diff --git a/public/icons/ios/167.png b/public/icons/ios/167.png
new file mode 100644
index 0000000..10bd49f
Binary files /dev/null and b/public/icons/ios/167.png differ
diff --git a/public/icons/ios/180.png b/public/icons/ios/180.png
new file mode 100644
index 0000000..6a7354e
Binary files /dev/null and b/public/icons/ios/180.png differ
diff --git a/public/icons/ios/192.png b/public/icons/ios/192.png
new file mode 100644
index 0000000..36cfea4
Binary files /dev/null and b/public/icons/ios/192.png differ
diff --git a/public/icons/ios/20.png b/public/icons/ios/20.png
new file mode 100644
index 0000000..ed8ea60
Binary files /dev/null and b/public/icons/ios/20.png differ
diff --git a/public/icons/ios/256.png b/public/icons/ios/256.png
new file mode 100644
index 0000000..541a39c
Binary files /dev/null and b/public/icons/ios/256.png differ
diff --git a/public/icons/ios/29.png b/public/icons/ios/29.png
new file mode 100644
index 0000000..6459279
Binary files /dev/null and b/public/icons/ios/29.png differ
diff --git a/public/icons/ios/32.png b/public/icons/ios/32.png
new file mode 100644
index 0000000..8a17039
Binary files /dev/null and b/public/icons/ios/32.png differ
diff --git a/public/icons/ios/40.png b/public/icons/ios/40.png
new file mode 100644
index 0000000..a99fd92
Binary files /dev/null and b/public/icons/ios/40.png differ
diff --git a/public/icons/ios/50.png b/public/icons/ios/50.png
new file mode 100644
index 0000000..47686ce
Binary files /dev/null and b/public/icons/ios/50.png differ
diff --git a/public/icons/ios/512.png b/public/icons/ios/512.png
new file mode 100644
index 0000000..a66d3cc
Binary files /dev/null and b/public/icons/ios/512.png differ
diff --git a/public/icons/ios/57.png b/public/icons/ios/57.png
new file mode 100644
index 0000000..da9e025
Binary files /dev/null and b/public/icons/ios/57.png differ
diff --git a/public/icons/ios/58.png b/public/icons/ios/58.png
new file mode 100644
index 0000000..d033957
Binary files /dev/null and b/public/icons/ios/58.png differ
diff --git a/public/icons/ios/60.png b/public/icons/ios/60.png
new file mode 100644
index 0000000..5faec7f
Binary files /dev/null and b/public/icons/ios/60.png differ
diff --git a/public/icons/ios/64.png b/public/icons/ios/64.png
new file mode 100644
index 0000000..28ed296
Binary files /dev/null and b/public/icons/ios/64.png differ
diff --git a/public/icons/ios/72.png b/public/icons/ios/72.png
new file mode 100644
index 0000000..4032614
Binary files /dev/null and b/public/icons/ios/72.png differ
diff --git a/public/icons/ios/76.png b/public/icons/ios/76.png
new file mode 100644
index 0000000..d58c10f
Binary files /dev/null and b/public/icons/ios/76.png differ
diff --git a/public/icons/ios/80.png b/public/icons/ios/80.png
new file mode 100644
index 0000000..1343a39
Binary files /dev/null and b/public/icons/ios/80.png differ
diff --git a/public/icons/ios/87.png b/public/icons/ios/87.png
new file mode 100644
index 0000000..a1ff725
Binary files /dev/null and b/public/icons/ios/87.png differ
diff --git a/public/icons/windows11/LargeTile.scale-100.png b/public/icons/windows11/LargeTile.scale-100.png
new file mode 100644
index 0000000..10e82f7
Binary files /dev/null and b/public/icons/windows11/LargeTile.scale-100.png differ
diff --git a/public/icons/windows11/LargeTile.scale-125.png b/public/icons/windows11/LargeTile.scale-125.png
new file mode 100644
index 0000000..758f586
Binary files /dev/null and b/public/icons/windows11/LargeTile.scale-125.png differ
diff --git a/public/icons/windows11/LargeTile.scale-150.png b/public/icons/windows11/LargeTile.scale-150.png
new file mode 100644
index 0000000..ef3da6d
Binary files /dev/null and b/public/icons/windows11/LargeTile.scale-150.png differ
diff --git a/public/icons/windows11/LargeTile.scale-200.png b/public/icons/windows11/LargeTile.scale-200.png
new file mode 100644
index 0000000..ff3574d
Binary files /dev/null and b/public/icons/windows11/LargeTile.scale-200.png differ
diff --git a/public/icons/windows11/LargeTile.scale-400.png b/public/icons/windows11/LargeTile.scale-400.png
new file mode 100644
index 0000000..f52ec6f
Binary files /dev/null and b/public/icons/windows11/LargeTile.scale-400.png differ
diff --git a/public/icons/windows11/SmallTile.scale-100.png b/public/icons/windows11/SmallTile.scale-100.png
new file mode 100644
index 0000000..583ccdd
Binary files /dev/null and b/public/icons/windows11/SmallTile.scale-100.png differ
diff --git a/public/icons/windows11/SmallTile.scale-125.png b/public/icons/windows11/SmallTile.scale-125.png
new file mode 100644
index 0000000..31ed819
Binary files /dev/null and b/public/icons/windows11/SmallTile.scale-125.png differ
diff --git a/public/icons/windows11/SmallTile.scale-150.png b/public/icons/windows11/SmallTile.scale-150.png
new file mode 100644
index 0000000..8662bda
Binary files /dev/null and b/public/icons/windows11/SmallTile.scale-150.png differ
diff --git a/public/icons/windows11/SmallTile.scale-200.png b/public/icons/windows11/SmallTile.scale-200.png
new file mode 100644
index 0000000..7ea8d9d
Binary files /dev/null and b/public/icons/windows11/SmallTile.scale-200.png differ
diff --git a/public/icons/windows11/SmallTile.scale-400.png b/public/icons/windows11/SmallTile.scale-400.png
new file mode 100644
index 0000000..5815542
Binary files /dev/null and b/public/icons/windows11/SmallTile.scale-400.png differ
diff --git a/public/icons/windows11/SplashScreen.scale-100.png b/public/icons/windows11/SplashScreen.scale-100.png
new file mode 100644
index 0000000..132f68e
Binary files /dev/null and b/public/icons/windows11/SplashScreen.scale-100.png differ
diff --git a/public/icons/windows11/SplashScreen.scale-125.png b/public/icons/windows11/SplashScreen.scale-125.png
new file mode 100644
index 0000000..d2c74aa
Binary files /dev/null and b/public/icons/windows11/SplashScreen.scale-125.png differ
diff --git a/public/icons/windows11/SplashScreen.scale-150.png b/public/icons/windows11/SplashScreen.scale-150.png
new file mode 100644
index 0000000..85defba
Binary files /dev/null and b/public/icons/windows11/SplashScreen.scale-150.png differ
diff --git a/public/icons/windows11/SplashScreen.scale-200.png b/public/icons/windows11/SplashScreen.scale-200.png
new file mode 100644
index 0000000..2483f12
Binary files /dev/null and b/public/icons/windows11/SplashScreen.scale-200.png differ
diff --git a/public/icons/windows11/SplashScreen.scale-400.png b/public/icons/windows11/SplashScreen.scale-400.png
new file mode 100644
index 0000000..a39908f
Binary files /dev/null and b/public/icons/windows11/SplashScreen.scale-400.png differ
diff --git a/public/icons/windows11/Square150x150Logo.scale-100.png b/public/icons/windows11/Square150x150Logo.scale-100.png
new file mode 100644
index 0000000..d9a84ba
Binary files /dev/null and b/public/icons/windows11/Square150x150Logo.scale-100.png differ
diff --git a/public/icons/windows11/Square150x150Logo.scale-125.png b/public/icons/windows11/Square150x150Logo.scale-125.png
new file mode 100644
index 0000000..e300ffa
Binary files /dev/null and b/public/icons/windows11/Square150x150Logo.scale-125.png differ
diff --git a/public/icons/windows11/Square150x150Logo.scale-150.png b/public/icons/windows11/Square150x150Logo.scale-150.png
new file mode 100644
index 0000000..9d7d69e
Binary files /dev/null and b/public/icons/windows11/Square150x150Logo.scale-150.png differ
diff --git a/public/icons/windows11/Square150x150Logo.scale-200.png b/public/icons/windows11/Square150x150Logo.scale-200.png
new file mode 100644
index 0000000..16e6be9
Binary files /dev/null and b/public/icons/windows11/Square150x150Logo.scale-200.png differ
diff --git a/public/icons/windows11/Square150x150Logo.scale-400.png b/public/icons/windows11/Square150x150Logo.scale-400.png
new file mode 100644
index 0000000..95e4f87
Binary files /dev/null and b/public/icons/windows11/Square150x150Logo.scale-400.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-16.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-16.png
new file mode 100644
index 0000000..11a9a18
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-16.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-20.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-20.png
new file mode 100644
index 0000000..ed8ea60
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-20.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-24.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-24.png
new file mode 100644
index 0000000..8a493de
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-24.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-256.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-256.png
new file mode 100644
index 0000000..541a39c
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-256.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-30.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-30.png
new file mode 100644
index 0000000..7a9e3eb
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-30.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-32.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-32.png
new file mode 100644
index 0000000..8a17039
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-32.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-36.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-36.png
new file mode 100644
index 0000000..c4eb678
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-36.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-40.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-40.png
new file mode 100644
index 0000000..a99fd92
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-40.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-44.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-44.png
new file mode 100644
index 0000000..bc57479
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-44.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-48.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-48.png
new file mode 100644
index 0000000..2ce1505
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-48.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-60.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-60.png
new file mode 100644
index 0000000..5faec7f
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-60.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-64.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-64.png
new file mode 100644
index 0000000..28ed296
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-64.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-72.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-72.png
new file mode 100644
index 0000000..4032614
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-72.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-80.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-80.png
new file mode 100644
index 0000000..1343a39
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-80.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-96.png b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-96.png
new file mode 100644
index 0000000..2db923c
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-96.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-16.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-16.png
new file mode 100644
index 0000000..11a9a18
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-16.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-20.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-20.png
new file mode 100644
index 0000000..ed8ea60
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-20.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-24.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-24.png
new file mode 100644
index 0000000..8a493de
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-24.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-256.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-256.png
new file mode 100644
index 0000000..541a39c
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-256.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-30.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-30.png
new file mode 100644
index 0000000..7a9e3eb
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-30.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-32.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-32.png
new file mode 100644
index 0000000..8a17039
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-32.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-36.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-36.png
new file mode 100644
index 0000000..c4eb678
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-36.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-40.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-40.png
new file mode 100644
index 0000000..a99fd92
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-40.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-44.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-44.png
new file mode 100644
index 0000000..bc57479
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-44.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-48.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-48.png
new file mode 100644
index 0000000..2ce1505
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-48.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-60.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-60.png
new file mode 100644
index 0000000..5faec7f
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-60.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-64.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-64.png
new file mode 100644
index 0000000..28ed296
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-64.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-72.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-72.png
new file mode 100644
index 0000000..4032614
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-72.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-80.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-80.png
new file mode 100644
index 0000000..1343a39
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-80.png differ
diff --git a/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-96.png b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-96.png
new file mode 100644
index 0000000..2db923c
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.altform-unplated_targetsize-96.png differ
diff --git a/public/icons/windows11/Square44x44Logo.scale-100.png b/public/icons/windows11/Square44x44Logo.scale-100.png
new file mode 100644
index 0000000..bc57479
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.scale-100.png differ
diff --git a/public/icons/windows11/Square44x44Logo.scale-125.png b/public/icons/windows11/Square44x44Logo.scale-125.png
new file mode 100644
index 0000000..579bb8e
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.scale-125.png differ
diff --git a/public/icons/windows11/Square44x44Logo.scale-150.png b/public/icons/windows11/Square44x44Logo.scale-150.png
new file mode 100644
index 0000000..a79062b
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.scale-150.png differ
diff --git a/public/icons/windows11/Square44x44Logo.scale-200.png b/public/icons/windows11/Square44x44Logo.scale-200.png
new file mode 100644
index 0000000..cc2b984
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.scale-200.png differ
diff --git a/public/icons/windows11/Square44x44Logo.scale-400.png b/public/icons/windows11/Square44x44Logo.scale-400.png
new file mode 100644
index 0000000..40b3ea0
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.scale-400.png differ
diff --git a/public/icons/windows11/Square44x44Logo.targetsize-16.png b/public/icons/windows11/Square44x44Logo.targetsize-16.png
new file mode 100644
index 0000000..11a9a18
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-16.png differ
diff --git a/public/icons/windows11/Square44x44Logo.targetsize-20.png b/public/icons/windows11/Square44x44Logo.targetsize-20.png
new file mode 100644
index 0000000..ed8ea60
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-20.png differ
diff --git a/public/icons/windows11/Square44x44Logo.targetsize-24.png b/public/icons/windows11/Square44x44Logo.targetsize-24.png
new file mode 100644
index 0000000..8a493de
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-24.png differ
diff --git a/public/icons/windows11/Square44x44Logo.targetsize-256.png b/public/icons/windows11/Square44x44Logo.targetsize-256.png
new file mode 100644
index 0000000..541a39c
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-256.png differ
diff --git a/public/icons/windows11/Square44x44Logo.targetsize-30.png b/public/icons/windows11/Square44x44Logo.targetsize-30.png
new file mode 100644
index 0000000..7a9e3eb
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-30.png differ
diff --git a/public/icons/windows11/Square44x44Logo.targetsize-32.png b/public/icons/windows11/Square44x44Logo.targetsize-32.png
new file mode 100644
index 0000000..8a17039
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-32.png differ
diff --git a/public/icons/windows11/Square44x44Logo.targetsize-36.png b/public/icons/windows11/Square44x44Logo.targetsize-36.png
new file mode 100644
index 0000000..c4eb678
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-36.png differ
diff --git a/public/icons/windows11/Square44x44Logo.targetsize-40.png b/public/icons/windows11/Square44x44Logo.targetsize-40.png
new file mode 100644
index 0000000..a99fd92
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-40.png differ
diff --git a/public/icons/windows11/Square44x44Logo.targetsize-44.png b/public/icons/windows11/Square44x44Logo.targetsize-44.png
new file mode 100644
index 0000000..bc57479
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-44.png differ
diff --git a/public/icons/windows11/Square44x44Logo.targetsize-48.png b/public/icons/windows11/Square44x44Logo.targetsize-48.png
new file mode 100644
index 0000000..2ce1505
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-48.png differ
diff --git a/public/icons/windows11/Square44x44Logo.targetsize-60.png b/public/icons/windows11/Square44x44Logo.targetsize-60.png
new file mode 100644
index 0000000..5faec7f
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-60.png differ
diff --git a/public/icons/windows11/Square44x44Logo.targetsize-64.png b/public/icons/windows11/Square44x44Logo.targetsize-64.png
new file mode 100644
index 0000000..28ed296
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-64.png differ
diff --git a/public/icons/windows11/Square44x44Logo.targetsize-72.png b/public/icons/windows11/Square44x44Logo.targetsize-72.png
new file mode 100644
index 0000000..4032614
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-72.png differ
diff --git a/public/icons/windows11/Square44x44Logo.targetsize-80.png b/public/icons/windows11/Square44x44Logo.targetsize-80.png
new file mode 100644
index 0000000..1343a39
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-80.png differ
diff --git a/public/icons/windows11/Square44x44Logo.targetsize-96.png b/public/icons/windows11/Square44x44Logo.targetsize-96.png
new file mode 100644
index 0000000..2db923c
Binary files /dev/null and b/public/icons/windows11/Square44x44Logo.targetsize-96.png differ
diff --git a/public/icons/windows11/StoreLogo.scale-100.png b/public/icons/windows11/StoreLogo.scale-100.png
new file mode 100644
index 0000000..26e771c
Binary files /dev/null and b/public/icons/windows11/StoreLogo.scale-100.png differ
diff --git a/public/icons/windows11/StoreLogo.scale-125.png b/public/icons/windows11/StoreLogo.scale-125.png
new file mode 100644
index 0000000..30feca0
Binary files /dev/null and b/public/icons/windows11/StoreLogo.scale-125.png differ
diff --git a/public/icons/windows11/StoreLogo.scale-150.png b/public/icons/windows11/StoreLogo.scale-150.png
new file mode 100644
index 0000000..7e85df0
Binary files /dev/null and b/public/icons/windows11/StoreLogo.scale-150.png differ
diff --git a/public/icons/windows11/StoreLogo.scale-200.png b/public/icons/windows11/StoreLogo.scale-200.png
new file mode 100644
index 0000000..0ad49c5
Binary files /dev/null and b/public/icons/windows11/StoreLogo.scale-200.png differ
diff --git a/public/icons/windows11/StoreLogo.scale-400.png b/public/icons/windows11/StoreLogo.scale-400.png
new file mode 100644
index 0000000..9b0f28f
Binary files /dev/null and b/public/icons/windows11/StoreLogo.scale-400.png differ
diff --git a/public/icons/windows11/Wide310x150Logo.scale-100.png b/public/icons/windows11/Wide310x150Logo.scale-100.png
new file mode 100644
index 0000000..da4d109
Binary files /dev/null and b/public/icons/windows11/Wide310x150Logo.scale-100.png differ
diff --git a/public/icons/windows11/Wide310x150Logo.scale-125.png b/public/icons/windows11/Wide310x150Logo.scale-125.png
new file mode 100644
index 0000000..f57db8e
Binary files /dev/null and b/public/icons/windows11/Wide310x150Logo.scale-125.png differ
diff --git a/public/icons/windows11/Wide310x150Logo.scale-150.png b/public/icons/windows11/Wide310x150Logo.scale-150.png
new file mode 100644
index 0000000..ddd8dc0
Binary files /dev/null and b/public/icons/windows11/Wide310x150Logo.scale-150.png differ
diff --git a/public/icons/windows11/Wide310x150Logo.scale-200.png b/public/icons/windows11/Wide310x150Logo.scale-200.png
new file mode 100644
index 0000000..132f68e
Binary files /dev/null and b/public/icons/windows11/Wide310x150Logo.scale-200.png differ
diff --git a/public/icons/windows11/Wide310x150Logo.scale-400.png b/public/icons/windows11/Wide310x150Logo.scale-400.png
new file mode 100644
index 0000000..2483f12
Binary files /dev/null and b/public/icons/windows11/Wide310x150Logo.scale-400.png differ
diff --git a/public/logo.png b/public/logo.png
new file mode 100644
index 0000000..634024c
Binary files /dev/null and b/public/logo.png differ
diff --git a/public/logo.svg b/public/logo.svg
new file mode 100644
index 0000000..22bdd70
--- /dev/null
+++ b/public/logo.svg
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/public/logo_circle.png b/public/logo_circle.png
new file mode 100644
index 0000000..3db4993
Binary files /dev/null and b/public/logo_circle.png differ
diff --git a/public/manifest.json b/public/manifest.json
new file mode 100644
index 0000000..59acc2d
--- /dev/null
+++ b/public/manifest.json
@@ -0,0 +1,80 @@
+{
+ "name": "SplitPro",
+ "short_name": "SplitPro",
+ "description": "Split Expenses with your friends for free. SplitPro is an open-source alternative for splitwise",
+ "icons": [
+ {
+ "src": "/icons/ios/72.png",
+ "type": "image/png",
+ "sizes": "72x72"
+ },
+ {
+ "src": "/icons/ios/192.png",
+ "sizes": "192x192",
+ "type": "image/png",
+ "purpose": "any maskable"
+ },
+ {
+ "src": "/icons/ios/256.png",
+ "sizes": "256x256",
+ "type": "image/png",
+ "purpose": "any maskable"
+ },
+ {
+ "src": "/icons/ios/512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ }
+ ],
+ "theme_color": "#030711",
+ "background_color": "#030711",
+ "start_url": "/balances",
+ "display": "standalone",
+ "orientation": "portrait",
+ "screenshots": [
+ {
+ "src": "/Desktop.webp",
+ "type": "image/webp",
+ "sizes": "2910x1920",
+ "form_factor": "wide"
+ },
+ {
+ "src": "/hero.webp",
+ "type": "image/webp",
+ "sizes": "1125x2436",
+ "form_factor": "narrow"
+ }
+ ],
+ "shortcuts": [
+ {
+ "short_name": "Add expense",
+ "name": "Add expense",
+ "description": "Add expense with a friend or groups",
+ "url": "/add",
+ "icons": [
+ {
+ "src": "/icons/ios/72.png",
+ "type": "image/png",
+ "sizes": "72x72"
+ },
+ {
+ "src": "/icons/ios/192.png",
+ "sizes": "192x192",
+ "type": "image/png",
+ "purpose": "any maskable"
+ },
+ {
+ "src": "/icons/ios/256.png",
+ "sizes": "256x256",
+ "type": "image/png",
+ "purpose": "any maskable"
+ },
+ {
+ "src": "/icons/ios/512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ }
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/public/manifest/icon-192x192.png b/public/manifest/icon-192x192.png
new file mode 100644
index 0000000..d49e4ab
Binary files /dev/null and b/public/manifest/icon-192x192.png differ
diff --git a/public/manifest/icon-256x256.png b/public/manifest/icon-256x256.png
new file mode 100644
index 0000000..4f50a8c
Binary files /dev/null and b/public/manifest/icon-256x256.png differ
diff --git a/public/manifest/icon-384x384.png b/public/manifest/icon-384x384.png
new file mode 100644
index 0000000..0d166b0
Binary files /dev/null and b/public/manifest/icon-384x384.png differ
diff --git a/public/manifest/icon-512x512.png b/public/manifest/icon-512x512.png
new file mode 100644
index 0000000..1513965
Binary files /dev/null and b/public/manifest/icon-512x512.png differ
diff --git a/public/manifest/manifest.webmanifest b/public/manifest/manifest.webmanifest
new file mode 100644
index 0000000..9471556
--- /dev/null
+++ b/public/manifest/manifest.webmanifest
@@ -0,0 +1,32 @@
+{
+ "theme_color": "#ffffff",
+ "background_color": "#ffffff",
+ "display": "standalone",
+ "scope": "/",
+ "start_url": "/balances",
+ "name": "SplitPro",
+ "short_name": "SplitPro",
+ "description": "Split Expenses with your friends for free",
+ "icons": [
+ {
+ "src": "/icon-192x192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "/icon-256x256.png",
+ "sizes": "256x256",
+ "type": "image/png"
+ },
+ {
+ "src": "/icon-384x384.png",
+ "sizes": "384x384",
+ "type": "image/png"
+ },
+ {
+ "src": "/icon-512x512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/public/og_banner.png b/public/og_banner.png
new file mode 100644
index 0000000..528c979
Binary files /dev/null and b/public/og_banner.png differ
diff --git a/public/sitemap.xml b/public/sitemap.xml
new file mode 100644
index 0000000..323b515
--- /dev/null
+++ b/public/sitemap.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+ https://splitpro.app/
+ 2024-04-03T05:23:09+00:00
+ 1.00
+
+
+ https://splitpro.app/blog/need-for-splitwise-alternative
+ 2024-04-03T05:23:09+00:00
+ 1
+
+
+ https://splitpro.app/terms
+ 2024-04-03T05:23:09+00:00
+ 0.80
+
+
+ https://splitpro.app/privacy
+ 2024-04-03T05:23:09+00:00
+ 0.80
+
+
+ https://splitpro.app/auth/signin
+ 2024-04-03T05:23:09+00:00
+ 0.80
+
+
+ https://splitpro.app/balances
+ 2024-04-03T05:23:09+00:00
+ 0.64
+
+
+
+
\ No newline at end of file
diff --git a/src/components/Account/SubmitFeedback.tsx b/src/components/Account/SubmitFeedback.tsx
new file mode 100644
index 0000000..1fd291f
--- /dev/null
+++ b/src/components/Account/SubmitFeedback.tsx
@@ -0,0 +1,88 @@
+import React, { useState } from 'react';
+import { AppDrawer } from '../ui/drawer';
+import { ChevronRight, MessageSquare } from 'lucide-react';
+import { api } from '~/utils/api';
+import { Textarea } from '../ui/textarea';
+import { z } from 'zod';
+import { useForm } from 'react-hook-form';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { Form, FormControl, FormField, FormItem, FormMessage } from '../ui/form';
+import { toast } from 'sonner';
+
+const feedbackSchema = z.object({
+ feedback: z
+ .string({ required_error: 'Feedback is required' })
+ .min(10, { message: 'Feedback should be at least 10 characters' }),
+});
+
+export const SubmitFeedback: React.FC = () => {
+ const submitFeedbackMutation = api.user.submitFeedback.useMutation();
+ const [feedbackOpen, setFeedbackOpen] = useState(false);
+
+ const feedbackForm = useForm>({
+ resolver: zodResolver(feedbackSchema),
+ });
+
+ async function onGroupSubmit(values: z.infer) {
+ try {
+ await submitFeedbackMutation.mutateAsync({ feedback: values.feedback });
+ feedbackForm.reset();
+ toast.success('Feedback submitted', { duration: 1500 });
+ } catch (e) {
+ toast.error('Failed to submit feedback');
+ }
+ setFeedbackOpen(false);
+ }
+
+ return (
+
+
+
+ Submit feedback
+
+
+
+ }
+ open={feedbackOpen}
+ onOpenChange={setFeedbackOpen}
+ onClose={() => setFeedbackOpen(false)}
+ leftAction="Close"
+ title="Submit a feedback"
+ className="h-[70vh]"
+ shouldCloseOnAction={false}
+ actionTitle="Submit"
+ actionOnClick={async () => {
+ await feedbackForm.handleSubmit(onGroupSubmit)();
+ }}
+ >
+
+
+
+
+
+ );
+};
diff --git a/src/components/Account/SubscribeNotification.tsx b/src/components/Account/SubscribeNotification.tsx
new file mode 100644
index 0000000..bc5d3a4
--- /dev/null
+++ b/src/components/Account/SubscribeNotification.tsx
@@ -0,0 +1,115 @@
+import React, { useEffect, useState } from 'react';
+import { Button } from '../ui/button';
+import { toast } from 'sonner';
+import { env } from '~/env';
+import { Bell, BellOff, ChevronRight } from 'lucide-react';
+import { api } from '~/utils/api';
+import { useAppStore } from '~/store/appStore';
+
+const base64ToUint8Array = (base64: string) => {
+ const padding = '='.repeat((4 - (base64.length % 4)) % 4);
+ const b64 = (base64 + padding).replace(/-/g, '+').replace(/_/g, '/');
+
+ const rawData = window.atob(b64);
+ const outputArray = new Uint8Array(rawData.length);
+
+ for (let i = 0; i < rawData.length; ++i) {
+ outputArray[i] = rawData.charCodeAt(i);
+ }
+ return outputArray;
+};
+
+export const SubscribeNotification: React.FC = () => {
+ const updatePushSubscription = api.user.updatePushNotification.useMutation();
+ const [isSubscribed, setIsSubscribed] = useState(false);
+ const webPushPublicKey = useAppStore((s) => s.webPushPublicKey);
+
+ useEffect(() => {
+ if (typeof window !== 'undefined' && 'serviceWorker' in navigator) {
+ // run only in browser
+ navigator.serviceWorker.ready
+ .then((reg) => {
+ reg.pushManager
+ .getSubscription()
+ .then((sub) => {
+ if (sub) {
+ setIsSubscribed(true);
+ }
+ })
+ .catch(console.error);
+ })
+ .catch(console.error);
+ }
+ }, []);
+
+ async function onRequestNotification() {
+ try {
+ const result = await Notification.requestPermission();
+ if (result === 'granted') {
+ toast.success('You will receive notifications now');
+ navigator.serviceWorker.ready
+ .then(async (reg) => {
+ if (!webPushPublicKey) {
+ toast.error('Notification is not supported');
+ return;
+ }
+
+ const sub = await reg.pushManager.subscribe({
+ userVisibleOnly: true,
+ applicationServerKey: base64ToUint8Array(webPushPublicKey),
+ });
+
+ setIsSubscribed(true);
+ updatePushSubscription.mutate({ subscription: JSON.stringify(sub) });
+ })
+ .catch((e) => {
+ toast.error('Cannot subscribe to notification');
+ });
+ }
+ } catch (e) {
+ toast.error('Error requesting notification');
+ }
+ }
+
+ async function unSubscribeNotification() {
+ try {
+ const reg = await navigator.serviceWorker.ready;
+ const sub = await reg.pushManager.getSubscription();
+ if (sub) {
+ await sub.unsubscribe();
+ setIsSubscribed(false);
+ }
+ } catch (e) {
+ toast.error('Error unsubscribing notification');
+ }
+ }
+
+ if (!webPushPublicKey) {
+ return null;
+ }
+
+ return (
+ <>
+
+
+ {!isSubscribed ? (
+ <>
+
+ Enable Notification
+ >
+ ) : (
+ <>
+
+ Disable notification
+ >
+ )}
+
+
+
+ >
+ );
+};
diff --git a/src/components/Account/UpdateDetails.tsx b/src/components/Account/UpdateDetails.tsx
new file mode 100644
index 0000000..deae248
--- /dev/null
+++ b/src/components/Account/UpdateDetails.tsx
@@ -0,0 +1,81 @@
+import React, { useState } from 'react';
+import { AppDrawer } from '../ui/drawer';
+import { ChevronRight, MessageSquare, Pencil } from 'lucide-react';
+import { api } from '~/utils/api';
+import { Textarea } from '../ui/textarea';
+import { z } from 'zod';
+import { useForm } from 'react-hook-form';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { Form, FormControl, FormField, FormItem, FormMessage } from '../ui/form';
+import { Input } from '../ui/input';
+import { toast } from 'sonner';
+
+const detailsSchema = z.object({
+ name: z.string({ required_error: 'Name is required' }).min(1, { message: 'Name is required' }),
+});
+
+export const UpdateDetails: React.FC<{ defaultName: string }> = ({ defaultName }) => {
+ const updateDetailsMutation = api.user.updateUserDetail.useMutation();
+ const [updateDetailsOpen, setUpdateDetailsOpen] = useState(false);
+
+ const detailForm = useForm>({
+ resolver: zodResolver(detailsSchema),
+ defaultValues: {
+ name: defaultName,
+ },
+ });
+
+ const utils = api.useUtils();
+
+ async function onGroupSubmit(values: z.infer) {
+ try {
+ await updateDetailsMutation.mutateAsync({ name: values.name });
+ toast.success('Updated details', { duration: 1500 });
+ utils.user.me.refetch().catch(console.error);
+ } catch (error) {
+ toast.error('Error in updating details');
+
+ console.error(error);
+ }
+ setUpdateDetailsOpen(false);
+ }
+
+ return (
+ }
+ onTriggerClick={() => setUpdateDetailsOpen(true)}
+ open={updateDetailsOpen}
+ onClose={() => setUpdateDetailsOpen(false)}
+ onOpenChange={(open) => setUpdateDetailsOpen(open)}
+ leftAction="Close"
+ title="Edit details"
+ className="h-[80vh]"
+ actionTitle="Save"
+ actionOnClick={async () => {
+ await detailForm.handleSubmit(onGroupSubmit)();
+ }}
+ >
+
+
+
+ (
+
+
+
+
+
+
+ )}
+ />
+
+
+
+
+ );
+};
diff --git a/src/components/AddExpense/AddExpensePage.tsx b/src/components/AddExpense/AddExpensePage.tsx
new file mode 100644
index 0000000..c4e568e
--- /dev/null
+++ b/src/components/AddExpense/AddExpensePage.tsx
@@ -0,0 +1,457 @@
+import { useRouter } from 'next/router';
+import React from 'react';
+import { useAddExpenseStore } from '~/store/addStore';
+import { api } from '~/utils/api';
+import { UserInput } from './UserInput';
+import { SelectUserOrGroup } from './SelectUserOrGroup';
+import { AppDrawer, DrawerClose } from '../ui/drawer';
+import { Button } from '../ui/button';
+import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem } from '../ui/command';
+import { Banknote, CalendarIcon, Check, HeartHandshakeIcon } from 'lucide-react';
+import { Input } from '../ui/input';
+import { SplitTypeSection } from './SplitTypeSection';
+import { Popover, PopoverContent, PopoverTrigger } from '../ui/popover';
+import { cn } from '~/lib/utils';
+import { format } from 'date-fns';
+import { Calendar } from '../ui/calendar';
+import UploadFile from './UploadFile';
+import { CategoryIcons } from '../ui/categoryIcons';
+import Link from 'next/link';
+import { CURRENCIES } from '~/lib/currency';
+import { env } from '~/env';
+
+const categories = {
+ entertainment: {
+ name: 'Entertainment',
+ items: [
+ {
+ games: 'Games',
+ movies: 'Movies',
+ music: 'Music',
+ sports: 'Sports',
+ other: 'Entertainment',
+ },
+ ],
+ },
+ food: {
+ name: 'Food & Drinks',
+ items: [
+ {
+ diningOut: 'Dining Out',
+ groceries: 'Groceries',
+ liquor: 'Liquor',
+ other: 'Food & Drinks',
+ },
+ ],
+ },
+ home: {
+ name: 'Home',
+ items: [
+ {
+ electronics: 'Electronics',
+ furniture: 'Furniture',
+ supplies: 'Supplies',
+ maintenance: 'Maintenance',
+ mortgage: 'Mortgage',
+ pets: 'Pets',
+ rent: 'Rent',
+ services: 'Services',
+ other: 'Home',
+ },
+ ],
+ },
+ life: {
+ name: 'Life',
+ items: [
+ {
+ childcare: 'Childcare',
+ clothing: 'Clothing',
+ education: 'Education',
+ gifts: 'Gifts',
+ medical: 'Medical',
+ taxes: 'Taxes',
+ other: 'Life',
+ },
+ ],
+ },
+ travel: {
+ name: 'Travel',
+ items: [
+ {
+ bus: 'Bus',
+ train: 'Train',
+ car: 'Car',
+ fuel: 'Fuel',
+ parking: 'Parking',
+ plane: 'Plane',
+ taxi: 'Taxi',
+ other: 'Travel',
+ },
+ ],
+ },
+ utilities: {
+ name: 'Utilities',
+ items: [
+ {
+ cleaning: 'Cleaning',
+ electricity: 'Electricity',
+ gas: 'Gas',
+ internet: 'Internet',
+ trash: 'Trash',
+ phone: 'Phone',
+ water: 'Water',
+ other: 'Utilities',
+ },
+ ],
+ },
+};
+
+export const AddOrEditExpensePage: React.FC<{
+ isStorageConfigured: boolean;
+ enableSendingInvites: boolean;
+ expenseId?: string;
+}> = ({ isStorageConfigured, enableSendingInvites, expenseId }) => {
+ const [open, setOpen] = React.useState(false);
+
+ const showFriends = useAddExpenseStore((s) => s.showFriends);
+ const amount = useAddExpenseStore((s) => s.amount);
+ const participants = useAddExpenseStore((s) => s.participants);
+ const group = useAddExpenseStore((s) => s.group);
+ const currency = useAddExpenseStore((s) => s.currency);
+ const category = useAddExpenseStore((s) => s.category);
+ const description = useAddExpenseStore((s) => s.description);
+ const isFileUploading = useAddExpenseStore((s) => s.isFileUploading);
+ const amtStr = useAddExpenseStore((s) => s.amountStr);
+ const expenseDate = useAddExpenseStore((s) => s.expenseDate);
+
+ const {
+ setCurrency,
+ setCategory,
+ setDescription,
+ setAmount,
+ setAmountStr,
+ resetState,
+ setSplitScreenOpen,
+ setExpenseDate,
+ } = useAddExpenseStore((s) => s.actions);
+
+ const addExpenseMutation = api.user.addOrEditExpense.useMutation();
+ const addGroupExpenseMutation = api.group.addOrEditExpense.useMutation();
+ const updateProfile = api.user.updateUserDetail.useMutation();
+
+ const router = useRouter();
+
+ function onUpdateAmount(amt: string) {
+ const _amt = amt.replace(',', '.');
+ setAmountStr(_amt);
+ setAmount(Number(_amt) || 0);
+ }
+
+ function addExpense() {
+ const { group, paidBy, splitType, fileKey, canSplitScreenClosed } =
+ useAddExpenseStore.getState();
+ if (!paidBy) {
+ return;
+ }
+
+ if (!canSplitScreenClosed) {
+ setSplitScreenOpen(true);
+ return;
+ }
+
+ if (group) {
+ addGroupExpenseMutation.mutate(
+ {
+ name: description,
+ currency,
+ amount,
+ groupId: group.id,
+ splitType,
+ participants: participants.map((p) => ({
+ userId: p.id,
+ amount: p.amount ?? 0,
+ })),
+ paidBy: paidBy.id,
+ category,
+ fileKey,
+ expenseDate,
+ expenseId,
+ },
+ {
+ onSuccess: (d) => {
+ if (d) {
+ router
+ .push(`/groups/${group.id}/expenses/${d?.id ?? expenseId}`)
+ .then(() => resetState())
+ .catch(console.error);
+ }
+ },
+ },
+ );
+ } else {
+ addExpenseMutation.mutate(
+ {
+ expenseId,
+ name: description,
+ currency,
+ amount,
+ splitType,
+ participants: participants.map((p) => ({
+ userId: p.id,
+ amount: p.amount ?? 0,
+ })),
+ paidBy: paidBy.id,
+ category,
+ fileKey,
+ expenseDate,
+ },
+ {
+ onSuccess: (d) => {
+ if (participants[1] && d) {
+ router
+ .push(`expenses/${d?.id ?? expenseId}`)
+ .then(() => resetState())
+ .catch(console.error);
+ }
+ },
+ },
+ );
+ }
+ }
+
+ const CategoryIcon = CategoryIcons[category] ?? Banknote;
+
+ return (
+ <>
+
+
+ {participants.length === 1 ? (
+
+
+ Cancel
+
+
+ ) : (
+
+ Cancel
+
+ )}
+
Add new expense
+
+ Save
+ {' '}
+
+
+ {showFriends || (participants.length === 1 && !group) ? (
+
+ ) : (
+ <>
+
+ }
+ title="Categories"
+ className="h-[70vh]"
+ shouldCloseOnAction
+ >
+
+ {Object.entries(categories).map(([categoryName, categoryDetails]) => {
+ return (
+
+
{categoryDetails.name}
+
+ {categoryDetails.items.map((item, index) =>
+ Object.entries(item).map(([key, value]) => {
+ const Icon =
+ CategoryIcons[key] ?? CategoryIcons[categoryName] ?? Banknote;
+ return (
+
+ {
+ setCategory(key === 'other' ? categoryName : key);
+ }}
+ >
+
+
+
+ {value}
+
+
+ );
+ }),
+ )}
+
+
+ );
+ })}
+
+
+
setDescription(e.target.value.toString() ?? '')}
+ className="text-lg placeholder:text-sm"
+ />
+
+
+
+ {currency ?? 'USD'}
+
+ }
+ onTriggerClick={() => setOpen(true)}
+ title="Select currency"
+ className="h-[70vh]"
+ shouldCloseOnAction
+ open={open}
+ onOpenChange={(openVal) => {
+ if (openVal !== open) setOpen(openVal);
+ }}
+ >
+
+
+
+ No currency found.
+
+ {CURRENCIES.map((framework) => (
+ {
+ const _currency = currentValue.split('-')[0]?.toUpperCase() ?? 'USD';
+ updateProfile.mutate({ currency: _currency });
+
+ setCurrency(_currency);
+ setOpen(false);
+ }}
+ >
+
+
+
{framework.name}
+
{framework.code}
+
+
+ ))}
+
+
+
+
+
+ onUpdateAmount(e.target.value)}
+ />
+
+ {!amount || description === '' ? (
+
+ ) : (
+
+
+
+
+
+
+
+
+
+ {expenseDate ? (
+ format(expenseDate, 'yyyy-MM-dd') === format(new Date(), 'yyyy-MM-dd') ? (
+ 'Today'
+ ) : (
+ format(expenseDate, 'MMM dd')
+ )
+ ) : (
+ Pick a date
+ )}
+
+
+
+
+
+
+
+
+ {isStorageConfigured ? : null}
+
+ addExpense()}
+ >
+ Submit
+
+
+
+
+ )}
+
+
+
+
+
+ Sponsor us
+
+
+
+
+ >
+ )}
+
+ >
+ );
+};
diff --git a/src/components/AddExpense/NoUsers.tsx b/src/components/AddExpense/NoUsers.tsx
new file mode 100644
index 0000000..e69de29
diff --git a/src/components/AddExpense/SelectUserOrGroup.tsx b/src/components/AddExpense/SelectUserOrGroup.tsx
new file mode 100644
index 0000000..92eff6a
--- /dev/null
+++ b/src/components/AddExpense/SelectUserOrGroup.tsx
@@ -0,0 +1,184 @@
+import { api } from '~/utils/api';
+import { GroupAvatar, UserAvatar } from '../ui/avatar';
+import { useAddExpenseStore } from '~/store/addStore';
+import { z } from 'zod';
+import { Button } from '../ui/button';
+import { UserPlusIcon } from '@heroicons/react/24/solid';
+import { CheckIcon } from '@heroicons/react/24/outline';
+import { type Group, type GroupUser, type User } from '@prisma/client';
+import { motion } from 'framer-motion';
+import Image from 'next/image';
+import { SendIcon } from 'lucide-react';
+import { env } from '~/env';
+import React from 'react';
+
+export const SelectUserOrGroup: React.FC<{
+ enableSendingInvites: boolean;
+}> = ({ enableSendingInvites }) => {
+ const nameOrEmail = useAddExpenseStore((s) => s.nameOrEmail);
+ const participants = useAddExpenseStore((s) => s.participants);
+ const group = useAddExpenseStore((s) => s.group);
+ const { addOrUpdateParticipant, removeParticipant, setNameOrEmail, setGroup, setParticipants } =
+ useAddExpenseStore((s) => s.actions);
+
+ const friendsQuery = api.user.getFriends.useQuery();
+ const groupsQuery = api.group.getAllGroups.useQuery();
+ const addFriendMutation = api.user.inviteFriend.useMutation();
+
+ const isEmail = z.string().email().safeParse(nameOrEmail);
+
+ const filteredGroups = groupsQuery.data?.filter((g) =>
+ g.group.name.toLowerCase().includes(nameOrEmail.toLowerCase()),
+ );
+ const filteredFriends = friendsQuery.data?.filter((f) =>
+ (f.name ?? f.email)?.toLowerCase().includes(nameOrEmail.toLowerCase()),
+ );
+
+ function onAddEmailClick(invite = false) {
+ if (isEmail.success) {
+ addFriendMutation.mutate(
+ { email: nameOrEmail, sendInviteEmail: invite },
+ {
+ onSuccess: (user) => {
+ removeParticipant(-1);
+ addOrUpdateParticipant(user);
+ setNameOrEmail('');
+ },
+ },
+ );
+ addOrUpdateParticipant({
+ id: -1,
+ name: nameOrEmail,
+ email: nameOrEmail,
+ emailVerified: new Date(),
+ image: null,
+ currency: 'USD',
+ });
+ // add email to split pro
+ }
+ }
+
+ function onGroupSelect(group: Group & { groupUsers: Array }) {
+ setGroup(group);
+ const { currentUser } = useAddExpenseStore.getState();
+ if (currentUser) {
+ setParticipants([
+ currentUser,
+ ...group.groupUsers.map((gu) => gu.user).filter((u) => u.id !== currentUser.id),
+ ]);
+ }
+ setNameOrEmail('');
+ }
+
+ if (group) {
+ return (
+ You can have only one group at a time
+ );
+ }
+
+ return (
+
+
+
+ {enableSendingInvites ? (
+
+ {isEmail.success
+ ? "Warning: Don't use send invite if it's invalid email. use add to Split Easy instead. Your account will be blocked if this feature is misused"
+ : null}
+
+ ) : (
+
Note: sending invite is disabled for now because of spam
+ )}
+
+
+ {enableSendingInvites && (
+ onAddEmailClick(false)}
+ >
+
+ Send invite to user
+
+ )}
+ onAddEmailClick(false)}
+ >
+
+ Add to Split Easy
+
+
+
+
+ {filteredFriends?.length ? (
+ <>
+
Friends
+ {filteredFriends.map((f) => {
+ const isExisting = participants.some((p) => p.id === f.id);
+
+ return (
+
{
+ if (isExisting) {
+ removeParticipant(f.id);
+ } else {
+ addOrUpdateParticipant(f);
+ }
+ setNameOrEmail('');
+ }}
+ >
+
+
+
{f.name ?? f.email}
+
+ {participants.some((p) => p.id === f.id) ? (
+
+
+
+ ) : null}
+
+ );
+ })}
+ >
+ ) : null}
+
+ {/*Can't select multiple groups or groups with outside ppl */}
+ {filteredGroups?.length && participants.length === 1 ? (
+ <>
+
Groups
+
+ {filteredGroups.map((g) => (
+
onGroupSelect(g.group)}
+ >
+
+
+ ))}
+
+ >
+ ) : null}
+
+ {filteredFriends?.length === 0 && filteredGroups?.length === 0 ? (
+
+
+
+ ) : null}
+
+
+ );
+};
diff --git a/src/components/AddExpense/SplitTypeSection.tsx b/src/components/AddExpense/SplitTypeSection.tsx
new file mode 100644
index 0000000..7b355f3
--- /dev/null
+++ b/src/components/AddExpense/SplitTypeSection.tsx
@@ -0,0 +1,417 @@
+import { type Participant, useAddExpenseStore } from '~/store/addStore';
+import { AppDrawer, Drawer, DrawerClose } from '../ui/drawer';
+import { UserAvatar } from '../ui/avatar';
+import { BarChart2, Check, DollarSign, Equal, Percent, Plus, X } from 'lucide-react';
+import { Tabs, TabsContent, TabsList, TabsTrigger } from '../ui/tabs';
+import { Input } from '../ui/input';
+import { useState } from 'react';
+import { SplitType } from '@prisma/client';
+
+export const SplitTypeSection: React.FC = () => {
+ const paidBy = useAddExpenseStore((s) => s.paidBy);
+ const participants = useAddExpenseStore((s) => s.participants);
+ const currentUser = useAddExpenseStore((s) => s.currentUser);
+ const canSplitScreenClosed = useAddExpenseStore((s) => s.canSplitScreenClosed);
+ const splitType = useAddExpenseStore((s) => s.splitType);
+ const splitScreenOpen = useAddExpenseStore((s) => s.splitScreenOpen);
+
+ const { setPaidBy, setSplitScreenOpen } = useAddExpenseStore((s) => s.actions);
+
+ return (
+
+
Paid by
+
+ {
+ (currentUser?.id === paidBy?.id ? 'you' : paidBy?.name ?? paidBy?.email)?.split(
+ ' ',
+ )[0]
+ }
+
+ }
+ title="Paid by"
+ className="h-[70vh]"
+ shouldCloseOnAction
+ >
+
+ {participants.map((participant) => (
+
setPaidBy(participant)}
+ >
+
+
+
{participant.name ?? participant.email ?? ''}
+
+ {participant.id === paidBy?.id ? : null}
+
+ ))}
+
+
+
+
and
+
+ {splitType === SplitType.EQUAL ? 'split equally' : `split unequally`}
+
+ }
+ title={splitType.charAt(0).toUpperCase() + splitType.slice(1).toLowerCase()}
+ className="h-[85vh] lg:h-[70vh]"
+ shouldCloseOnAction
+ dismissible={false}
+ actionTitle="Save"
+ actionDisabled={!canSplitScreenClosed}
+ open={splitScreenOpen}
+ onOpenChange={(open) => setSplitScreenOpen(open)}
+ >
+
+
+
+ );
+};
+
+const SplitExpenseForm: React.FC = () => {
+ const splitType = useAddExpenseStore((s) => s.splitType);
+ const { setSplitType } = useAddExpenseStore((s) => s.actions);
+
+ return (
+
+
setSplitType(v as SplitType)}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+const SplitEqualSection: React.FC = () => {
+ const participants = useAddExpenseStore((s) => s.participants);
+ const currency = useAddExpenseStore((s) => s.currency);
+ const amount = useAddExpenseStore((s) => s.amount);
+ const canSplitScreenClosed = useAddExpenseStore((s) => s.canSplitScreenClosed);
+ const { addOrUpdateParticipant } = useAddExpenseStore((s) => s.actions);
+
+ const totalParticipants = participants.filter((p) => p.splitShare !== 0).length;
+
+ const selectAll = () => {
+ const allSelected = participants.every((p) => p.splitShare !== 0);
+ participants.forEach((p) => {
+ addOrUpdateParticipant({ ...p, splitShare: allSelected ? 0 : 1 });
+ });
+ };
+
+ const allSelected = participants.every((p) => p.splitShare !== 0);
+
+ return (
+
+
+
+
+ {currency} {(amount / totalParticipants).toFixed(2)} per person
+
+
+
+
+
+ {allSelected ? : }
+ All
+
+
+ {participants.map((p) => (
+
addOrUpdateParticipant({ ...p, splitShare: p.splitShare === 0 ? 1 : 0 })}
+ >
+
+ {p.splitShare !== 0 ? (
+
+
+
+ ) : null}
+
+ ))}
+
+ );
+};
+
+const SplitByPercentageSection: React.FC = () => {
+ const participants = useAddExpenseStore((s) => s.participants);
+ const { addOrUpdateParticipant } = useAddExpenseStore((s) => s.actions);
+ const canSplitScreenClosed = useAddExpenseStore((s) => s.canSplitScreenClosed);
+ const currency = useAddExpenseStore((s) => s.currency);
+
+ const [splitShareValue, setSplitShareValue] = useState(
+ participants.reduce(
+ (acc, p) => {
+ acc[p.id] = p.splitShare?.toString();
+ return acc;
+ },
+ {} as Record,
+ ),
+ );
+
+ const handleSplitShareChange = (p: Participant, value: string) => {
+ setSplitShareValue({ ...splitShareValue, [p.id]: value });
+ if (value === '' || isNaN(parseFloat(value))) {
+ addOrUpdateParticipant({ ...p, splitShare: 0 });
+ return;
+ }
+ addOrUpdateParticipant({ ...p, splitShare: parseFloat(value) });
+ };
+
+ const remainingPercentage = 100 - participants.reduce((acc, p) => acc + (p.splitShare ?? 0), 0);
+
+ return (
+
+
+ Remaining {remainingPercentage}%
+
+ {participants.map((p) => (
+
+
+
+
+ handleSplitShareChange(p, e.target.value)}
+ />
+ {' '}%
+
+
+ ))}
+
+ );
+};
+
+const SplitByAmountSection: React.FC = () => {
+ const participants = useAddExpenseStore((s) => s.participants);
+ const currency = useAddExpenseStore((s) => s.currency);
+ const amount = useAddExpenseStore((s) => s.amount);
+ const { addOrUpdateParticipant } = useAddExpenseStore((s) => s.actions);
+ const canSplitScreenClosed = useAddExpenseStore((s) => s.canSplitScreenClosed);
+
+ const [splitShareValue, setSplitShareValue] = useState(
+ participants.reduce(
+ (acc, p) => {
+ acc[p.id] = p.splitShare?.toString() ?? '';
+ return acc;
+ },
+ {} as Record,
+ ),
+ );
+
+ const handleSplitShareChange = (p: Participant, value: string) => {
+ setSplitShareValue({ ...splitShareValue, [p.id]: value });
+ if (value === '' || isNaN(parseFloat(value))) {
+ addOrUpdateParticipant({ ...p, splitShare: 0 });
+ return;
+ }
+ const formattedValue = parseFloat(parseFloat(value).toFixed(2));
+ addOrUpdateParticipant({ ...p, splitShare: formattedValue });
+ };
+
+ const totalSplitShare = participants.reduce((acc, p) => acc + (p.splitShare ?? 0), 0);
+
+ const remainingAmount = parseFloat((amount - totalSplitShare).toFixed(2));
+
+ return (
+
+
+ Remaining {currency} {remainingAmount}
+
+ {participants.map((p) => (
+
+
+
+ {p.name ?? p.email}
+
+
+
{currency}
+
handleSplitShareChange(p, e.target.value)}
+ />
+
+
+ ))}
+
+ );
+};
+
+const SplitByShareSection: React.FC = () => {
+ const participants = useAddExpenseStore((s) => s.participants);
+ const { addOrUpdateParticipant } = useAddExpenseStore((s) => s.actions);
+ const currency = useAddExpenseStore((s) => s.currency);
+
+ const [splitShareValue, setSplitShareValue] = useState(
+ participants.reduce(
+ (acc, p) => {
+ acc[p.id] = p.splitShare?.toString();
+ return acc;
+ },
+ {} as Record,
+ ),
+ );
+
+ const handleSplitShareChange = (p: Participant, value: string) => {
+ setSplitShareValue({ ...splitShareValue, [p.id]: value });
+ if (value === '' || isNaN(parseFloat(value))) {
+ addOrUpdateParticipant({ ...p, splitShare: 0 });
+ return;
+ }
+ addOrUpdateParticipant({ ...p, splitShare: parseFloat(value) });
+ };
+
+ const totalShare = participants.reduce((acc, p) => acc + (p.splitShare ?? 0), 0);
+
+ return (
+
+
Total shares {totalShare}
+ {participants.map((p) => (
+
+
+
+
handleSplitShareChange(p, e.target.value)}
+ />
+
Share(s)
+
+
+ ))}
+
+ );
+};
+
+const SplitByAdjustmentSection: React.FC = () => {
+ const participants = useAddExpenseStore((s) => s.participants);
+ const currency = useAddExpenseStore((s) => s.currency);
+ const amount = useAddExpenseStore((s) => s.amount);
+ const { addOrUpdateParticipant } = useAddExpenseStore((s) => s.actions);
+ const canSplitScreenClosed = useAddExpenseStore((s) => s.canSplitScreenClosed);
+
+ const [splitShareValue, setSplitShareValue] = useState(
+ participants.reduce(
+ (acc, p) => {
+ acc[p.id] = p.splitShare?.toString();
+ return acc;
+ },
+ {} as Record,
+ ),
+ );
+
+ const handleSplitShareChange = (p: Participant, value: string) => {
+ setSplitShareValue({ ...splitShareValue, [p.id]: value });
+ if (value === '' || isNaN(parseFloat(value))) {
+ addOrUpdateParticipant({ ...p, splitShare: 0 });
+ return;
+ }
+ addOrUpdateParticipant({ ...p, splitShare: parseFloat(value) });
+ };
+
+ const remainingPercentage =
+ amount - participants.reduce((acc, p) => acc + (p.splitShare ?? 0), 0);
+
+ return (
+
+
+ {' '}
+ Remaining to split equally {currency} {remainingPercentage}
+
+ {participants.map((p) => (
+
+
+
+
{currency}
+
+
handleSplitShareChange(p, e.target.value)}
+ />
+
+
+ ))}
+
+ );
+};
+
+export const UserAndAmount: React.FC<{ user: Participant; currency: string }> = ({
+ user,
+ currency,
+}) => {
+ const paidBy = useAddExpenseStore((s) => s.paidBy);
+ const amount = useAddExpenseStore((s) => s.amount);
+
+ const shareAmount = paidBy?.id === user.id ? (user.amount ?? 0) - amount : user.amount;
+
+ return (
+
+
+
+
{user.name ?? user.email}
+
+ {(shareAmount ?? 0) > 0 ? '-' : ''} {currency} {Math.abs(shareAmount ?? 0).toFixed(2)}
+
+
+
+ );
+};
diff --git a/src/components/AddExpense/UploadFile.tsx b/src/components/AddExpense/UploadFile.tsx
new file mode 100644
index 0000000..08c0c5e
--- /dev/null
+++ b/src/components/AddExpense/UploadFile.tsx
@@ -0,0 +1,97 @@
+import React, { useState } from 'react';
+import { Input } from '../ui/input';
+import { Label } from '../ui/label';
+import { type File, FileUp, ImagePlus, Image as ImageUploaded } from 'lucide-react';
+import { useAddExpenseStore } from '~/store/addStore';
+import { api } from '~/utils/api';
+import { FILE_SIZE_LIMIT } from '~/lib/constants';
+import { toast } from 'sonner';
+
+const getImgHeightAndWidth = (file: File) => {
+ return new Promise<{ width: number; height: number }>((resolve, reject) => {
+ const img = new Image();
+ img.src = URL.createObjectURL(file);
+ img.onload = () => {
+ resolve({ width: img.width, height: img.height });
+ URL.revokeObjectURL(img.src);
+ };
+ img.onerror = (error) => {
+ reject(error);
+ };
+ });
+};
+
+export const UploadFile: React.FC = () => {
+ const [file, setFile] = useState(null);
+ const { setFileUploading, setFileKey } = useAddExpenseStore((s) => s.actions);
+
+ const getUploadUrl = api.user.getUploadUrl.useMutation();
+
+ const handleFileChange = async (event: React.ChangeEvent) => {
+ const files = event.target.files;
+
+ const file = files?.[0];
+
+ if (!file) return;
+
+ if (file.size > FILE_SIZE_LIMIT) {
+ toast.error(`File should be less than ${FILE_SIZE_LIMIT / 1024 / 1024}MB`);
+ return;
+ }
+
+ setFile(file);
+
+ const { height, width } = await getImgHeightAndWidth(file);
+ console.log('height:', height, 'width:', width);
+
+ setFileUploading(true);
+ try {
+ const { fileUrl, key } = await getUploadUrl.mutateAsync({
+ fileName: file.name,
+ fileType: file.type,
+ fileSize: file.size,
+ });
+
+ const response = await fetch(fileUrl, {
+ method: 'PUT',
+ body: file,
+ });
+
+ if (!response.ok) {
+ toast.error('Failed to upload file');
+ return;
+ }
+
+ toast.success('File uploaded successfully');
+
+ setFileKey(key);
+ console.log('Setting file key', key);
+ } catch (error) {
+ console.error('Error getting upload url:', error);
+ toast.error(`Error uploading file`);
+ }
+
+ setFileUploading(false);
+ };
+
+ return (
+
+
+ {file ? (
+
+ ) : (
+
+ )}
+
+
+
+ );
+};
+
+export default UploadFile;
diff --git a/src/components/AddExpense/UserInput.tsx b/src/components/AddExpense/UserInput.tsx
new file mode 100644
index 0000000..0e29321
--- /dev/null
+++ b/src/components/AddExpense/UserInput.tsx
@@ -0,0 +1,107 @@
+import { useAddExpenseStore } from '~/store/addStore';
+import { GroupAvatar, UserAvatar } from '../ui/avatar';
+import { z } from 'zod';
+import { api } from '~/utils/api';
+import Router from 'next/router';
+
+export const UserInput: React.FC<{
+ isEditing?: boolean;
+}> = ({ isEditing }) => {
+ const {
+ setNameOrEmail,
+ removeLastParticipant,
+ removeParticipant,
+ addOrUpdateParticipant,
+ setParticipants,
+ setGroup,
+ } = useAddExpenseStore((s) => s.actions);
+ const nameOrEmail = useAddExpenseStore((s) => s.nameOrEmail);
+ const participants = useAddExpenseStore((s) => s.participants);
+ const currentUser = useAddExpenseStore((s) => s.currentUser);
+ const group = useAddExpenseStore((s) => s.group);
+
+ const addFriendMutation = api.user.inviteFriend.useMutation();
+
+ const isEmail = z.string().email().safeParse(nameOrEmail);
+
+ const handleKeyDown = (e: React.KeyboardEvent) => {
+ if (e.key === 'Backspace' && nameOrEmail === '') {
+ if (group) {
+ const currentPath = window.location.pathname;
+ const searchParams = new URLSearchParams(window.location.search);
+ searchParams.delete('groupId');
+ Router.push(`${currentPath}?${searchParams.toString()}`).catch(console.error);
+
+ setGroup(undefined);
+ console.log('set Remove called 1', group);
+
+ if (currentUser) {
+ setParticipants([currentUser]);
+ }
+ } else {
+ removeLastParticipant(); // Assuming deleteUser is the function you want to call
+ }
+ } else if (e.key === 'Enter' && isEmail.success) {
+ addFriendMutation.mutate(
+ { email: nameOrEmail.toLowerCase() },
+ {
+ onSuccess: (user) => {
+ removeParticipant(-1);
+ addOrUpdateParticipant(user);
+ setNameOrEmail('');
+ },
+ },
+ );
+ addOrUpdateParticipant({
+ id: -1,
+ name: nameOrEmail,
+ email: nameOrEmail,
+ emailVerified: new Date(),
+ image: null,
+ currency: 'USD',
+ });
+ }
+ };
+
+ return (
+
+ {group ? (
+
+ ) : (
+ participants.map((p) =>
+ p.id !== currentUser?.id ? (
+
+
+
{p.name ?? p.email}
+
+ ) : null,
+ )
+ )}
+
+
1
+ ? 'Add more friends'
+ : 'Search friends, groups or add email'
+ }
+ value={nameOrEmail}
+ onChange={(e) => setNameOrEmail(e.target.value)}
+ onKeyDown={handleKeyDown}
+ className="min-w-[100px] flex-grow bg-transparent outline-none placeholder:text-sm focus:ring-0"
+ autoFocus
+ disabled={isEditing && !!group}
+ />
+
+ );
+};
diff --git a/src/components/Expense/DeleteExpense.tsx b/src/components/Expense/DeleteExpense.tsx
new file mode 100644
index 0000000..3d6eec7
--- /dev/null
+++ b/src/components/Expense/DeleteExpense.tsx
@@ -0,0 +1,66 @@
+import React from 'react';
+import { Button } from '../ui/button';
+import { Trash2 } from 'lucide-react';
+import { api } from '~/utils/api';
+import { useRouter } from 'next/router';
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+ AlertDialogTrigger,
+} from '../ui/alert-dialog';
+
+export const DeleteExpense: React.FC<{
+ expenseId: string;
+ friendId?: number;
+ groupId?: number;
+}> = ({ expenseId, friendId, groupId }) => {
+ const router = useRouter();
+
+ const deleteExpenseMutation = api.user.deleteExpense.useMutation();
+
+ const onDeleteExpense = async () => {
+ await deleteExpenseMutation.mutateAsync({ expenseId });
+ if (groupId) {
+ await router.replace(`/groups/${groupId}`);
+ return;
+ }
+ if (friendId) {
+ await router.replace(`/balances/${friendId}`);
+ return;
+ }
+
+ await router.replace(`/balances`);
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+ Are you absolutely sure?
+
+ This action cannot be undone. This will permanently delete your expense.
+
+
+
+ Cancel
+
+ Delete
+
+
+
+
+
+ );
+};
diff --git a/src/components/Expense/ExpensePage.tsx b/src/components/Expense/ExpensePage.tsx
new file mode 100644
index 0000000..b12ea91
--- /dev/null
+++ b/src/components/Expense/ExpensePage.tsx
@@ -0,0 +1,126 @@
+import { type ExpenseParticipant, type Expense, type User } from '@prisma/client';
+import { type User as NextUser } from 'next-auth';
+import { format, isSameDay } from 'date-fns';
+import React from 'react';
+import { toUIString } from '~/utils/numbers';
+import { UserAvatar } from '../ui/avatar';
+import Image from 'next/image';
+import { AppDrawer, Drawer, DrawerContent, DrawerTrigger } from '../ui/drawer';
+import { Separator } from '../ui/separator';
+import { CategoryIcons } from '../ui/categoryIcons';
+import { Banknote } from 'lucide-react';
+import { env } from '~/env';
+
+type ExpenseDetailsProps = {
+ user: NextUser;
+ expense: Expense & {
+ expenseParticipants: Array;
+ addedByUser: User;
+ paidByUser: User;
+ deletedByUser: User | null;
+ updatedByUser: User | null;
+ };
+ storagePublicUrl?: string;
+};
+
+const ExpenseDetails: React.FC = ({ user, expense, storagePublicUrl }) => {
+ const youPaid = expense.paidBy === user.id;
+
+ const CategoryIcon = CategoryIcons[expense.category] ?? Banknote;
+
+ return (
+
+
+
+
+
+
+
+
{expense.name}
+
+ {expense.currency} {toUIString(expense.amount ?? 0)}
+
+ {!isSameDay(expense.expenseDate, expense.createdAt) ? (
+
{format(expense.expenseDate, 'dd MMM yyyy')}
+ ) : null}
+ {expense.updatedByUser ? (
+
+ Edited by {expense.updatedByUser?.name ?? expense.updatedByUser?.email} on{' '}
+ {format(expense.updatedAt, 'dd MMM yyyy')}
+
+ ) : null}
+ {expense.deletedByUser ? (
+
+ Deleted by {expense.deletedByUser.name ?? expense.addedByUser.email} on{' '}
+ {format(expense.deletedAt ?? expense.createdAt, 'dd MMM yyyy')}
+
+ ) : (
+
+ Added by {expense.addedByUser.name ?? expense.addedByUser.email} on{' '}
+ {format(expense.createdAt, 'dd MMM yyyy')}
+
+ )}
+
+
+
+ {expense.fileKey ? (
+
{
+ event.currentTarget.setAttribute('data-loaded', 'true');
+ }}
+ className=" h-14 w-14 rounded-md object-cover object-center data-[loaded=false]:animate-pulse data-[loaded=false]:bg-gray-100/10"
+ />
+ }
+ leftAction="Close"
+ title="Expense Receipt"
+ className="h-[98vh]"
+ >
+
+ {
+ event.currentTarget.setAttribute('data-loaded', 'true');
+ }}
+ className="h-full w-full rounded-2xl object-cover data-[loaded=false]:animate-pulse data-[loaded=false]:bg-gray-100/10"
+ />
+
+
+ ) : null}
+
+
+
+
+
+
+ {youPaid ? 'You' : expense.paidByUser.name ?? expense.paidByUser.email} paid{' '}
+ {expense.currency} {toUIString(expense.amount)}
+
+
+
+ {expense.expenseParticipants.map((p) => (
+
+
+
+ {user.id === p.userId ? 'You Owe' : `${p.user.name ?? p.user.email} owes`}{' '}
+ {expense.currency}{' '}
+ {toUIString((expense.paidBy === p.userId ? expense.amount ?? 0 : 0) - p.amount)}
+
+
+ ))}
+
+
+ );
+};
+
+export default ExpenseDetails;
diff --git a/src/components/Friend/DeleteFriend.tsx b/src/components/Friend/DeleteFriend.tsx
new file mode 100644
index 0000000..cc94199
--- /dev/null
+++ b/src/components/Friend/DeleteFriend.tsx
@@ -0,0 +1,80 @@
+import React, { useState } from 'react';
+import { Button } from '../ui/button';
+import { Trash2 } from 'lucide-react';
+import { api } from '~/utils/api';
+import { useRouter } from 'next/router';
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+ AlertDialogTrigger,
+} from '../ui/alert-dialog';
+import { toast } from 'sonner';
+
+export const DeleteFriend: React.FC<{
+ friendId: number;
+ disabled: boolean;
+}> = ({ friendId, disabled }) => {
+ const router = useRouter();
+ const [showTrigger, setShowTrigger] = useState(false);
+
+ const deleteFriendMutation = api.user.deleteFriend.useMutation();
+ const utils = api.useUtils();
+
+ const onDeleteFriend = async () => {
+ try {
+ await deleteFriendMutation.mutateAsync({ friendId });
+ } catch (e) {
+ toast.error('Failed to delete user');
+ return;
+ }
+ setShowTrigger(false);
+ utils.user.getBalances.invalidate().catch(console.error);
+
+ await router.replace(`/balances`);
+ };
+
+ return (
+
+
(status !== showTrigger ? setShowTrigger(status) : null)}
+ >
+
+
+
+
+
+
+
+ {disabled ? '' : 'Are you absolutely sure?'}
+
+ {disabled
+ ? "Can't remove friend with outstanding balances. Settle up first"
+ : 'Do you really want to continue'}
+
+
+
+ Cancel
+ {!disabled ? (
+
+ Delete
+
+ ) : null}
+
+
+
+
+ );
+};
diff --git a/src/components/Friend/Export.tsx b/src/components/Friend/Export.tsx
new file mode 100644
index 0000000..6191691
--- /dev/null
+++ b/src/components/Friend/Export.tsx
@@ -0,0 +1,120 @@
+import React from 'react';
+import { Button } from '~/components/ui/button';
+import { format } from 'date-fns';
+import { SplitType } from '@prisma/client';
+import { Download } from 'lucide-react';
+import { toUIString } from '~/utils/numbers';
+
+interface ExpenseParticipant {
+ expenseId: string;
+ userId: number;
+ amount: number;
+}
+
+interface Expense {
+ id: string;
+ paidBy: number;
+ addedBy: number;
+ name: string;
+ category: string;
+ amount: number;
+ splitType: SplitType;
+ expenseDate: Date;
+ createdAt: Date;
+ updatedAt: Date;
+ currency: string;
+ fileKey: string | null;
+ groupId: number | null;
+ deletedAt: Date | null;
+ deletedBy: number | null;
+ expenseParticipants: ExpenseParticipant[];
+}
+
+interface ExportCSVProps {
+ expenses: Expense[];
+ fileName: string;
+ currentUserId: number;
+ friendName: string;
+ friendId: string | number;
+ disabled?: boolean;
+}
+
+export const Export: React.FC = ({
+ expenses,
+ fileName,
+ currentUserId,
+ friendName,
+ friendId,
+ disabled = false,
+}) => {
+ const headers = [
+ 'Paid By',
+ 'Name',
+ 'Category',
+ 'Amount',
+ 'Split Type',
+ 'Expense Date',
+ 'Currency',
+ 'You Lent',
+ 'You Owe',
+ 'Settlement',
+ ];
+
+ const exportToCSV = () => {
+ const csvHeaders = headers.join(',');
+ const csvData = expenses.map((expense) => {
+ const youPaid = expense.paidBy === currentUserId;
+ const yourExpense = expense.expenseParticipants.find(
+ (p) => p.userId === (youPaid ? friendId : currentUserId),
+ );
+
+ const isSettlement = expense.splitType === SplitType.SETTLEMENT;
+
+ return [
+ expense.paidBy === currentUserId ? 'You' : friendName,
+ expense.name,
+ expense.category,
+ toUIString(expense?.amount ?? 0),
+ expense.splitType,
+ format(new Date(expense.expenseDate), 'yyyy-MM-dd HH:mm:ss'),
+ expense.currency,
+ youPaid && !isSettlement ? toUIString(yourExpense?.amount ?? 0) : 0,
+ !youPaid && !isSettlement ? toUIString(yourExpense?.amount ?? 0) : 0,
+ isSettlement ? toUIString(yourExpense?.amount ?? 0) : 0,
+ ];
+ });
+
+ const csvContent = [
+ csvHeaders,
+ ...csvData.map((row) =>
+ row
+ .map((cell) => (typeof cell === 'string' && cell.includes(',') ? `"${cell}"` : cell))
+ .join(','),
+ ),
+ ].join('\n');
+
+ const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
+ const link = document.createElement('a');
+ if (link.download !== undefined) {
+ const url = URL.createObjectURL(blob);
+ link.setAttribute('href', url);
+ link.setAttribute('download', `${fileName}.csv`);
+ link.style.visibility = 'hidden';
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ }
+ };
+
+ return (
+
+ Export
+
+ );
+};
diff --git a/src/components/Friend/FirendBalance.tsx b/src/components/Friend/FirendBalance.tsx
new file mode 100644
index 0000000..c2b38c0
--- /dev/null
+++ b/src/components/Friend/FirendBalance.tsx
@@ -0,0 +1,27 @@
+import { type Balance, type User } from '@prisma/client';
+import { UserAvatar } from '../ui/avatar';
+import clsx from 'clsx';
+import { toUIString } from '~/utils/numbers';
+
+export const FriendBalance: React.FC<{ user: User; balance: Balance }> = ({ user, balance }) => {
+ const isPositive = balance.amount > 0;
+
+ return (
+
+
+
+
+ {isPositive ? 'you get' : 'you owe'}
+
+
+ {balance.currency} {toUIString(balance.amount)}
+
+
+
+ );
+};
diff --git a/src/components/Friend/Settleup.tsx b/src/components/Friend/Settleup.tsx
new file mode 100644
index 0000000..9cb313a
--- /dev/null
+++ b/src/components/Friend/Settleup.tsx
@@ -0,0 +1,218 @@
+import React from 'react';
+import { Button } from '../ui/button';
+import { AppDrawer, Drawer, DrawerClose, DrawerContent, DrawerTrigger } from '../ui/drawer';
+import { type User, type Balance, SplitType } from '@prisma/client';
+import { type User as NextUser } from 'next-auth';
+import { FriendBalance } from './FirendBalance';
+import { Input } from '../ui/input';
+import { UserAvatar } from '../ui/avatar';
+import { ArrowRightIcon } from 'lucide-react';
+import { api } from '~/utils/api';
+import { toast } from 'sonner';
+import { toFixedNumber } from '~/utils/numbers';
+
+export const SettleUp: React.FC<{
+ balances: Array;
+ friend: User;
+ currentUser: NextUser;
+}> = ({ balances, friend, currentUser }) => {
+ const [balanceToSettle, setBallanceToSettle] = React.useState(
+ balances.length > 1 ? undefined : balances[0],
+ );
+ const [amount, setAmount] = React.useState(
+ balances.length > 1 ? '' : toFixedNumber(Math.abs(balances[0]?.amount ?? 0)).toString(),
+ );
+
+ const isCurrentUserPaying = (balanceToSettle?.amount ?? 0) < 0;
+
+ function onSelectBalance(balance: Balance) {
+ setBallanceToSettle(balance);
+ setAmount(toFixedNumber(Math.abs(balance.amount)).toString());
+ }
+
+ const addExpenseMutation = api.user.addOrEditExpense.useMutation();
+ const utils = api.useUtils();
+
+ function saveExpense() {
+ if (!balanceToSettle || !amount || !parseFloat(amount)) {
+ return;
+ }
+
+ addExpenseMutation.mutate(
+ {
+ name: 'Settle up',
+ currency: balanceToSettle.currency,
+ amount: parseFloat(amount),
+ splitType: SplitType.SETTLEMENT,
+ participants: [
+ {
+ userId: currentUser.id,
+ amount: isCurrentUserPaying ? parseFloat(amount) : -parseFloat(amount),
+ },
+ {
+ userId: friend.id,
+ amount: isCurrentUserPaying ? -parseFloat(amount) : parseFloat(amount),
+ },
+ ],
+ paidBy: isCurrentUserPaying ? currentUser.id : friend.id,
+ category: 'general',
+ },
+ {
+ onSuccess: () => {
+ utils.user.invalidate().catch(console.error);
+ },
+ onError: (error) => {
+ toast.info('Error while saving expense');
+ },
+ },
+ );
+ }
+
+ return (
+ <>
+
+ Settle up
+
+ }
+ disableTrigger={!balances?.length}
+ leftAction={''}
+ leftActionOnClick={() => {
+ setBallanceToSettle(undefined);
+ }}
+ title=""
+ className="h-[70vh]"
+ actionTitle=""
+ shouldCloseOnAction
+ >
+
+
+
+ {balanceToSettle ? (
+ balances.length > 1 ? (
+
setBallanceToSettle(undefined)}
+ >
+ Back
+
+ ) : (
+
+ (balances.length > 1 ? setBallanceToSettle(undefined) : null)}
+ >
+ Back
+
+
+ )
+ ) : (
+
+ )}
+
+
+ {balanceToSettle ? 'Settle up' : 'Select currency'}
+
+ {balanceToSettle ? (
+
+ saveExpense()}
+ >
+ Save
+
+
+ ) : (
+
+ )}
+
+ {!balanceToSettle ? (
+
+ {balances?.map((b) => (
+
onSelectBalance(b)}
+ className="cursor-pointer px-4 py-2"
+ >
+
+
+ ))}
+
+ ) : (
+
+
+
+
+ {isCurrentUserPaying
+ ? `You're paying ${friend.name ?? friend.email}`
+ : `${friend.name ?? friend.email} is paying you`}
+
+
+
+
{balanceToSettle.currency}
+
setAmount(e.target.value)}
+ />
+
+
+ )}
+
+
+ {balanceToSettle ? (
+ balances.length > 1 ? (
+
setBallanceToSettle(undefined)}
+ >
+ Back
+
+ ) : (
+
+ (balances.length > 1 ? setBallanceToSettle(undefined) : null)}
+ >
+ Back
+
+
+ )
+ ) : (
+
+ )}
+
+ {balanceToSettle ? (
+
+ saveExpense()}>
+ Save
+
+
+ ) : (
+
+ )}
+
+
+
+ >
+ );
+};
diff --git a/src/components/InstallApp.tsx b/src/components/InstallApp.tsx
new file mode 100644
index 0000000..376bd40
--- /dev/null
+++ b/src/components/InstallApp.tsx
@@ -0,0 +1,78 @@
+import React from 'react';
+import { Button } from './ui/button';
+import { Download } from 'lucide-react';
+import { AppDrawer } from './ui/drawer';
+
+const InstallApp: React.FC = () => {
+ const [isStandalone, setIsStandalone] = React.useState(false);
+
+ function isAppStandalone() {
+ // For iOS
+ if ('standalone' in navigator) {
+ return (navigator as unknown as { standalone: boolean }).standalone;
+ }
+ // For Android
+ if (window.matchMedia('(display-mode: standalone)').matches) {
+ return true;
+ }
+ // Fallback for browsers that don't support the above methods
+ return false;
+ }
+
+ React.useEffect(() => {
+ if (isAppStandalone()) {
+ setIsStandalone(true);
+ }
+ }, []);
+
+ if (isStandalone) {
+ return null;
+ }
+
+ return (
+ <>
+
+
+ Download App
+
+ }
+ leftAction="Close"
+ title="Download App"
+ className="h-[70vh]"
+ shouldCloseOnAction
+ >
+
+
You can download SplitPro as a PWA to your home screen
+
+
+ If you are using iOS, checkout this{' '}
+
+ video
+
+
+
+
+ If you are using Android, checkout this{' '}
+
+ Video
+
+
+
+
+
+ or
+ >
+ );
+};
+
+export default InstallApp;
diff --git a/src/components/Layout/MainLayout.tsx b/src/components/Layout/MainLayout.tsx
new file mode 100644
index 0000000..2b378c2
--- /dev/null
+++ b/src/components/Layout/MainLayout.tsx
@@ -0,0 +1,149 @@
+/* eslint-disable @typescript-eslint/prefer-optional-chain */
+import clsx from 'clsx';
+import { type LucideIcon } from 'lucide-react';
+import React from 'react';
+import {
+ ChartPieIcon as SolidScaleIcon,
+ UserGroupIcon as SolidUserGroupIcon,
+ PlusCircleIcon as SolidPlusCircleIcon,
+ UserCircleIcon as SolidUserCircleIcon,
+ ListBulletIcon as SolidListBulletIcon,
+} from '@heroicons/react/24/solid';
+import Link from 'next/link';
+import { useRouter } from 'next/router';
+
+interface MainLayoutProps {
+ title?: React.ReactNode;
+ children: React.ReactNode;
+ actions?: React.ReactNode;
+ header?: React.ReactNode;
+ hideAppBar?: boolean;
+}
+
+const MainLayout: React.FC = ({ children, actions, hideAppBar, title }) => {
+ const router = useRouter();
+ const currentPath = router.pathname;
+
+ return (
+
+
+
+
+ SplitPro
+
+
+
+
+
+
+
+
+ {title ? (
+
+ ) : null}
+ {children}
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+type NavItemProps = {
+ title: string;
+ Icon: LucideIcon;
+ link: string;
+ currentPath?: string;
+};
+
+const NavItem: React.FC = ({ title, Icon, link, currentPath }) => {
+ const isActive = currentPath?.startsWith(link);
+
+ return (
+
+
+
+ {title}
+
+
+ );
+};
+
+const NavItemDesktop: React.FC = ({ title, Icon, link, currentPath }) => {
+ const isActive = currentPath?.startsWith(link);
+
+ return (
+
+
+
+ {title}
+
+
+ );
+};
+export default MainLayout;
diff --git a/src/components/NotificationModal.tsx b/src/components/NotificationModal.tsx
new file mode 100644
index 0000000..5e8b9e9
--- /dev/null
+++ b/src/components/NotificationModal.tsx
@@ -0,0 +1,119 @@
+import React, { useEffect, useState } from 'react';
+import { toast } from 'sonner';
+import { env } from '~/env';
+import { Bell, BellOff, ChevronRight } from 'lucide-react';
+import { api } from '~/utils/api';
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+} from './ui/alert-dialog';
+import { useAppStore } from '~/store/appStore';
+
+const base64ToUint8Array = (base64: string) => {
+ const padding = '='.repeat((4 - (base64.length % 4)) % 4);
+ const b64 = (base64 + padding).replace(/-/g, '+').replace(/_/g, '/');
+
+ const rawData = window.atob(b64);
+ const outputArray = new Uint8Array(rawData.length);
+
+ for (let i = 0; i < rawData.length; ++i) {
+ outputArray[i] = rawData.charCodeAt(i);
+ }
+ return outputArray;
+};
+
+const NOTIFICATION_DISMISSED_TIME = 'notification_dismissed_time';
+const NOTIFICATION_DISMISSED_TIME_THRESHOLD = 1000 * 60 * 60 * 24 * 30; // 14 days
+
+export const NotificationModal: React.FC = () => {
+ const updatePushSubscription = api.user.updatePushNotification.useMutation();
+ const webPushPublicKey = useAppStore((s) => s.webPushPublicKey);
+
+ const [modalOpen, setModalOpen] = useState(false);
+
+ useEffect(() => {
+ if (typeof window !== 'undefined' && 'serviceWorker' in navigator) {
+ // run only in browser
+ navigator.serviceWorker.ready
+ .then((reg) => {
+ reg.pushManager
+ .getSubscription()
+ .then((sub) => {
+ if (!sub) {
+ const _notificationTime = localStorage.getItem(NOTIFICATION_DISMISSED_TIME);
+ if (
+ !_notificationTime ||
+ Date.now() - parseInt(_notificationTime) > NOTIFICATION_DISMISSED_TIME_THRESHOLD
+ ) {
+ setModalOpen(true);
+ }
+ }
+ })
+ .catch(console.error);
+ })
+ .catch(console.error);
+ }
+ }, []);
+
+ async function onRequestNotification() {
+ try {
+ const result = await Notification.requestPermission();
+ if (result === 'granted') {
+ toast.success('You will receive notifications now');
+ navigator.serviceWorker.ready
+ .then(async (reg) => {
+ if (!webPushPublicKey) {
+ return;
+ }
+ const sub = await reg.pushManager.subscribe({
+ userVisibleOnly: true,
+ applicationServerKey: base64ToUint8Array(webPushPublicKey),
+ });
+
+ updatePushSubscription.mutate({ subscription: JSON.stringify(sub) });
+ })
+ .catch((e) => {
+ toast.error('Cannot subscribe to notification');
+ });
+ setModalOpen(false);
+ }
+ } catch (e) {
+ toast.error('Error requesting notification');
+ }
+ }
+
+ function remindLater() {
+ localStorage.setItem(NOTIFICATION_DISMISSED_TIME, Date.now().toString());
+ setModalOpen(false);
+ }
+
+ if (!webPushPublicKey) {
+ return null;
+ }
+
+ return (
+
+
+
+ Enable notifications
+
+ Don't miss on important events. Subscribe to get notification for added expenses
+
+
+
+ Remind later
+
+
+ Subscribe
+
+
+
+
+ );
+};
diff --git a/src/components/group/AddMembers.tsx b/src/components/group/AddMembers.tsx
new file mode 100644
index 0000000..9180d2c
--- /dev/null
+++ b/src/components/group/AddMembers.tsx
@@ -0,0 +1,181 @@
+import React, { useState } from 'react';
+import { Button } from '~/components/ui/button';
+import { InformationCircleIcon, UserPlusIcon } from '@heroicons/react/24/solid';
+import { api } from '~/utils/api';
+import {
+ AppDrawer,
+ Drawer,
+ DrawerClose,
+ DrawerContent,
+ DrawerTrigger,
+} from '~/components/ui/drawer';
+import clsx from 'clsx';
+import { UserAvatar } from '../ui/avatar';
+import { type Group, type GroupUser } from '@prisma/client';
+import { CheckIcon, SendIcon, UserPlus } from 'lucide-react';
+import { Input } from '../ui/input';
+import { z } from 'zod';
+import { env } from '~/env';
+import { isStorageConfigured } from '~/server/storage';
+
+const AddMembers: React.FC<{
+ enableSendingInvites: boolean;
+ group: Group & { groupUsers: Array };
+ children: React.ReactNode;
+}> = ({ group, children, enableSendingInvites }) => {
+ const [open, setOpen] = useState(false);
+ const [userIds, setUserIds] = useState>({});
+ const [inputValue, setInputValue] = useState('');
+
+ const friendsQuery = api.user.getFriends.useQuery();
+ const addMembersMutation = api.group.addMembers.useMutation();
+ const addFriendMutation = api.user.inviteFriend.useMutation();
+
+ const utils = api.useUtils();
+
+ const groupUserMap = group.groupUsers.reduce(
+ (acc, gu) => {
+ acc[gu.userId] = true;
+ return acc;
+ },
+ {} as Record,
+ );
+
+ const filteredUsers = friendsQuery.data?.filter(
+ (f) =>
+ !groupUserMap[f.id] && (f.name ?? f.email)?.toLowerCase().includes(inputValue.toLowerCase()),
+ );
+
+ function onUserSelect(userId: number) {
+ setUserIds((prev) => ({ ...prev, [userId]: !prev[userId] }));
+ }
+
+ function onSave(userIds: Record) {
+ const users = [];
+
+ for (const userId of Object.keys(userIds)) {
+ if (userIds[parseInt(userId)]) {
+ users.push(parseInt(userId));
+ }
+ }
+
+ setInputValue('');
+
+ if (users.length === 0) {
+ return;
+ }
+
+ addMembersMutation.mutate(
+ {
+ groupId: group.id,
+ userIds: users,
+ },
+ {
+ onSuccess: () => {
+ utils.group.getGroupDetails.invalidate({ groupId: group.id }).catch(console.error);
+ },
+ },
+ );
+ setOpen(false);
+ setUserIds({});
+ }
+
+ const isEmail = z.string().email().safeParse(inputValue);
+
+ function onAddEmailClick(invite = false) {
+ if (isEmail.success) {
+ addFriendMutation.mutate(
+ { email: inputValue.toLowerCase(), sendInviteEmail: invite },
+ {
+ onSuccess: (user) => {
+ onSave({ ...userIds, [user.id]: true });
+ },
+ },
+ );
+ }
+ }
+
+ return (
+ {children}
+ }
+ onTriggerClick={() => setOpen(true)}
+ title="Add members"
+ leftAction="Cancel"
+ actionOnClick={() => onSave(userIds)}
+ className="h-[85vh]"
+ shouldCloseOnAction
+ actionTitle="Save"
+ open={open}
+ onClose={() => setOpen(false)}
+ onOpenChange={(state) => state !== open && setOpen(state)}
+ >
+
+
setInputValue(e.target.value)}
+ />
+ {!isEmail.success ? (
+
Enter valid email
+ ) : (
+
+ {enableSendingInvites ? (
+
+ Warning: Don't use send invite if it's invalid email. use add to Split Easy
+ instead. Your account will be blocked if this feature is misused
+
+ ) : (
+
Note: sending invite is disabled for now because of spam
+ )}
+
+
+ {enableSendingInvites && (
+ onAddEmailClick(true)}
+ >
+
+ {isEmail.success ? 'Send invite to user' : 'Enter valid email'}
+
+ )}
+ onAddEmailClick(false)}
+ >
+
+ {isEmail.success ? 'Add to Split Easy' : 'Enter valid email'}
+
+
+
+ )}
+
+ {filteredUsers?.map((friend) => (
+
onUserSelect(friend.id)}
+ >
+
+
+
{friend.name ?? friend.email}
+
+
+ {userIds[friend.id] ? : null}
+
+
+ ))}
+
+
+
+ );
+};
+
+export default AddMembers;
diff --git a/src/components/group/CreateGroup.tsx b/src/components/group/CreateGroup.tsx
new file mode 100644
index 0000000..c1e1c55
--- /dev/null
+++ b/src/components/group/CreateGroup.tsx
@@ -0,0 +1,91 @@
+import Avatar from 'boring-avatars';
+import React, { useState } from 'react';
+import { AppDrawer } from '~/components/ui/drawer';
+import { Input } from '~/components/ui/input';
+import { api } from '~/utils/api';
+import { useRouter } from 'next/router';
+import { z } from 'zod';
+import { useForm } from 'react-hook-form';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { Form, FormControl, FormField, FormItem, FormMessage } from '../ui/form';
+import { Button } from '../ui/button';
+
+const groupSchema = z.object({
+ name: z.string({ required_error: 'Name is required' }).min(1, { message: 'Name is required' }),
+});
+
+export const CreateGroup: React.FC<{ children: React.ReactNode }> = ({ children }) => {
+ const [drawerOpen, setDrawerOpen] = useState(false);
+
+ const createGroup = api.group.create.useMutation(undefined);
+ const utils = api.useUtils();
+
+ const groupForm = useForm>({
+ resolver: zodResolver(groupSchema),
+ });
+
+ const router = useRouter();
+
+ async function onGroupSubmit(values: z.infer) {
+ await createGroup.mutateAsync(
+ { name: values.name },
+ {
+ onSuccess: (data) => {
+ utils.group.getAllGroupsWithBalances.refetch().catch(console.error);
+ router
+ .push(`/groups/${data.id}`)
+ .then(() => setDrawerOpen(false))
+ .catch(console.error);
+ },
+ },
+ );
+ }
+
+ return (
+ <>
+ {
+ if (openVal !== drawerOpen) setDrawerOpen(openVal);
+ }}
+ trigger={children}
+ leftAction="Cancel"
+ leftActionOnClick={() => setDrawerOpen(false)}
+ title="Create a group"
+ className="h-[70vh]"
+ actionTitle="Submit"
+ actionOnClick={async () => {
+ await groupForm.handleSubmit(onGroupSubmit)();
+ }}
+ >
+
+
+ >
+ );
+};
diff --git a/src/components/group/GroupMyBalance.tsx b/src/components/group/GroupMyBalance.tsx
new file mode 100644
index 0000000..94021e3
--- /dev/null
+++ b/src/components/group/GroupMyBalance.tsx
@@ -0,0 +1,72 @@
+import { type GroupBalance, type User } from '@prisma/client';
+import React from 'react';
+import { toUIString } from '~/utils/numbers';
+
+type GroupMyBalanceProps = {
+ userId: number;
+ groupBalances: GroupBalance[];
+ users: User[];
+};
+
+const GroupMyBalance: React.FC = ({ userId, groupBalances, users }) => {
+ const userMap = users.reduce(
+ (acc, user) => {
+ acc[user.id] = user;
+ return acc;
+ },
+ {} as Record,
+ );
+
+ const cumulatedBalances = groupBalances.reduce(
+ (acc, balance) => {
+ if (balance.userId === userId && Math.abs(balance.amount) > 0) {
+ acc[balance.currency] = (acc[balance.currency] ?? 0) + balance.amount;
+ }
+ return acc;
+ },
+ {} as Record,
+ );
+
+ const youLent = Object.entries(cumulatedBalances).filter(([_, amount]) => amount > 0);
+ const youOwe = Object.entries(cumulatedBalances).filter(([_, amount]) => amount < 0);
+
+ return (
+
+
+ {youLent.length > 0 ? (
+
+ You lent
+ {youLent.map(([currency, amount], index, arr) => {
+ return (
+ <>
+
+ {currency} {toUIString(amount)}
+
+ {index < arr.length - 1 ?
+ : null}
+ >
+ );
+ })}
+
+ ) : null}
+
+ {youOwe.length > 0 ? (
+
+ You owe
+ {youOwe.map(([currency, amount], index, arr) => {
+ return (
+ <>
+
+ {currency} {toUIString(amount)}
+
+ {index < arr.length - 1 ?
+ : null}
+ >
+ );
+ })}
+
+ ) : null}
+
+
+ );
+};
+
+export default GroupMyBalance;
diff --git a/src/components/group/NoMembers.tsx b/src/components/group/NoMembers.tsx
new file mode 100644
index 0000000..05ad620
--- /dev/null
+++ b/src/components/group/NoMembers.tsx
@@ -0,0 +1,47 @@
+import React from 'react';
+import { PlusIcon } from '@heroicons/react/24/solid';
+import { Button } from '~/components/ui/button';
+import { Share, UserPlus } from 'lucide-react';
+import { type Group, type GroupUser } from '@prisma/client';
+import AddMembers from './AddMembers';
+
+const NoMembers: React.FC<{
+ group: Group & { groupUsers: Array };
+ enableSendingInvites: boolean;
+}> = ({ group, enableSendingInvites }) => {
+ const [isCopied, setIsCopied] = React.useState(false);
+
+ async function copyToClipboard() {
+ const inviteLink = `${window.location.origin}/join-group?groupId=${group.publicId}`;
+ await navigator.clipboard.writeText(inviteLink);
+ setIsCopied(true);
+ setTimeout(() => {
+ setIsCopied(false);
+ }, 2000);
+ }
+
+ return (
+
+
No members in the group yet.
+
+
+
+ Add Members
+
+
+
or
+
+ {!isCopied ? (
+ <>
+
+ Share invite link
+ >
+ ) : (
+ 'Copied'
+ )}
+
+
+ );
+};
+
+export default NoMembers;
diff --git a/src/components/theme-provider.tsx b/src/components/theme-provider.tsx
new file mode 100644
index 0000000..98e2886
--- /dev/null
+++ b/src/components/theme-provider.tsx
@@ -0,0 +1,9 @@
+'use client';
+
+import * as React from 'react';
+import { ThemeProvider as NextThemesProvider } from 'next-themes';
+import { type ThemeProviderProps } from 'next-themes/dist/types';
+
+export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
+ return {children} ;
+}
diff --git a/src/components/ui/alert-dialog.tsx b/src/components/ui/alert-dialog.tsx
new file mode 100644
index 0000000..0fc86f1
--- /dev/null
+++ b/src/components/ui/alert-dialog.tsx
@@ -0,0 +1,123 @@
+import * as React from 'react';
+import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
+
+import { cn } from '~/lib/utils';
+import { buttonVariants } from '~/components/ui/button';
+import { type VariantProps } from 'class-variance-authority';
+
+const AlertDialog = AlertDialogPrimitive.Root;
+
+const AlertDialogTrigger = AlertDialogPrimitive.Trigger;
+
+const AlertDialogPortal = AlertDialogPrimitive.Portal;
+
+const AlertDialogOverlay = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
+
+const AlertDialogContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+
+));
+AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
+
+const AlertDialogHeader = ({ className, ...props }: React.HTMLAttributes) => (
+
+);
+AlertDialogHeader.displayName = 'AlertDialogHeader';
+
+const AlertDialogFooter = ({ className, ...props }: React.HTMLAttributes) => (
+
+);
+AlertDialogFooter.displayName = 'AlertDialogFooter';
+
+const AlertDialogTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
+
+const AlertDialogDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName;
+
+const AlertDialogAction = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef & {
+ variant?: VariantProps['variant'];
+ size?: VariantProps['size'];
+ }
+>(({ className, variant, size, ...props }, ref) => (
+
+));
+AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
+
+const AlertDialogCancel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
+
+export {
+ AlertDialog,
+ AlertDialogPortal,
+ AlertDialogOverlay,
+ AlertDialogTrigger,
+ AlertDialogContent,
+ AlertDialogHeader,
+ AlertDialogFooter,
+ AlertDialogTitle,
+ AlertDialogDescription,
+ AlertDialogAction,
+ AlertDialogCancel,
+};
diff --git a/src/components/ui/avatar.tsx b/src/components/ui/avatar.tsx
new file mode 100644
index 0000000..cc52695
--- /dev/null
+++ b/src/components/ui/avatar.tsx
@@ -0,0 +1,90 @@
+import * as React from 'react';
+import * as AvatarPrimitive from '@radix-ui/react-avatar';
+import BoringAvatar from 'boring-avatars';
+
+import { cn } from '~/lib/utils';
+import { type User } from '@prisma/client';
+
+const Avatar = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+Avatar.displayName = AvatarPrimitive.Root.displayName;
+
+const AvatarImage = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+AvatarImage.displayName = AvatarPrimitive.Image.displayName;
+
+const AvatarFallback = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
+
+const UserAvatar: React.FC<{
+ user?: { name?: string | null; image?: string | null; email?: string | null } | null;
+ size?: number;
+}> = ({ user, size }) => {
+ return (
+
+
+
+
+
+
+ );
+};
+
+const GroupAvatar: React.FC<{ name: string; image?: string | null; size?: number }> = ({
+ name,
+ image,
+ size,
+}) => {
+ return (
+
+
+
+
+
+
+ );
+};
+
+export { Avatar, AvatarImage, AvatarFallback, UserAvatar, GroupAvatar };
diff --git a/src/components/ui/background-gradient.tsx b/src/components/ui/background-gradient.tsx
new file mode 100644
index 0000000..03d9716
--- /dev/null
+++ b/src/components/ui/background-gradient.tsx
@@ -0,0 +1,72 @@
+import React from 'react';
+import { motion } from 'framer-motion';
+import { cn } from '~/lib/utils';
+
+export const BackgroundGradient = ({
+ children,
+ className,
+ containerClassName,
+ animate = true,
+}: {
+ children?: React.ReactNode;
+ className?: string;
+ containerClassName?: string;
+ animate?: boolean;
+}) => {
+ const variants = {
+ initial: {
+ backgroundPosition: '0 50%',
+ },
+ animate: {
+ backgroundPosition: ['0, 50%', '100% 50%', '0 50%'],
+ },
+ };
+ return (
+
+ );
+};
diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx
new file mode 100644
index 0000000..888449a
--- /dev/null
+++ b/src/components/ui/button.tsx
@@ -0,0 +1,54 @@
+import * as React from 'react';
+import { Slot } from '@radix-ui/react-slot';
+import { cva, type VariantProps } from 'class-variance-authority';
+
+import { cn } from '~/lib/utils';
+import { LoadingSpinner } from './spinner';
+
+const buttonVariants = cva(
+ 'inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-35',
+ {
+ variants: {
+ variant: {
+ default: 'bg-primary text-primary-foreground hover:bg-primary/90',
+ destructive: 'bg-destructive text-destructive-foreground hover:bg-destructive/90',
+ outline: 'border border-input bg-background hover:bg-accent hover:text-accent-foreground',
+ secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
+ ghost: 'hover:text-primary/70 p-0',
+ link: 'text-primary underline-offset-4 hover:underline',
+ blue: 'text-primary bg-blue-700',
+ },
+ size: {
+ default: 'h-10 px-4 py-2',
+ sm: 'h-9 rounded-md px-3',
+ lg: 'h-11 rounded-md px-8',
+ icon: 'h-10 w-10',
+ },
+ },
+ defaultVariants: {
+ variant: 'default',
+ size: 'default',
+ },
+ },
+);
+
+export interface ButtonProps
+ extends React.ButtonHTMLAttributes,
+ VariantProps {
+ asChild?: boolean;
+ loading?: boolean;
+}
+
+const Button = React.forwardRef(
+ ({ className, variant, size, asChild = false, children, loading, ...props }, ref) => {
+ const Comp = asChild ? Slot : 'button';
+ return (
+
+ {loading ? : children}
+
+ );
+ },
+);
+Button.displayName = 'Button';
+
+export { Button, buttonVariants };
diff --git a/src/components/ui/calendar.tsx b/src/components/ui/calendar.tsx
new file mode 100644
index 0000000..b3f1657
--- /dev/null
+++ b/src/components/ui/calendar.tsx
@@ -0,0 +1,64 @@
+import * as React from "react"
+import { ChevronLeft, ChevronRight } from "lucide-react"
+import { DayPicker } from "react-day-picker"
+
+import { cn } from "~/lib/utils"
+import { buttonVariants } from "~/components/ui/button"
+
+export type CalendarProps = React.ComponentProps
+
+function Calendar({
+ className,
+ classNames,
+ showOutsideDays = true,
+ ...props
+}: CalendarProps) {
+ return (
+ ,
+ IconRight: ({ ...props }) => ,
+ }}
+ {...props}
+ />
+ )
+}
+Calendar.displayName = "Calendar"
+
+export { Calendar }
diff --git a/src/components/ui/categoryIcons.tsx b/src/components/ui/categoryIcons.tsx
new file mode 100644
index 0000000..28b7903
--- /dev/null
+++ b/src/components/ui/categoryIcons.tsx
@@ -0,0 +1,99 @@
+import {
+ Baby,
+ Backpack,
+ Banknote,
+ Bone,
+ Bus,
+ Car,
+ CarTaxiFront,
+ Construction,
+ FerrisWheel,
+ Flame,
+ Fuel,
+ Gamepad2,
+ Gift,
+ GlassWater,
+ Globe,
+ GraduationCap,
+ Hammer,
+ HandIcon,
+ Home,
+ type LucideIcon,
+ Music,
+ ParkingCircle,
+ Phone,
+ Pizza,
+ Plane,
+ Plug,
+ Popcorn,
+ Shirt,
+ ShoppingCart,
+ Sofa,
+ Sprout,
+ Stethoscope,
+ TrainFront,
+ Trophy,
+ Utensils,
+ UtilityPole,
+ Wine,
+ Zap,
+ type LucideProps,
+ Paintbrush,
+ Trash,
+ Wrench,
+} from 'lucide-react';
+import { type ForwardRefExoticComponent } from 'react';
+
+export const CategoryIcons: Record = {
+ games: Gamepad2,
+ movies: Popcorn,
+ music: Music,
+ sports: Trophy,
+ entertainment: FerrisWheel,
+ food: Pizza,
+ diningOut: Utensils,
+ groceries: ShoppingCart,
+ liquor: Wine,
+ home: Home,
+ electronics: Plug,
+ furniture: Sofa,
+ supplies: Sofa,
+ maintenance: Construction,
+ mortgage: HandIcon,
+ pets: Bone,
+ rent: Home,
+ services: Hammer,
+ life: Sprout,
+ childcare: Baby,
+ clothing: Shirt,
+ education: GraduationCap,
+ gifts: Gift,
+ medical: Stethoscope,
+ taxes: Banknote,
+ travel: Backpack,
+ bus: Bus,
+ train: TrainFront,
+ car: Car,
+ fuel: Fuel,
+ parking: ParkingCircle,
+ plane: Plane,
+ taxi: CarTaxiFront,
+ utilities: Wrench,
+ electricity: Zap,
+ gas: Flame,
+ internet: Globe,
+ phone: Phone,
+ water: GlassWater,
+ general: Banknote,
+ cleaning: Paintbrush,
+ trash: Trash,
+};
+
+export const CategoryIcon: React.FC<{ category: string; className?: string }> = ({
+ category,
+ ...props
+}) => {
+ const Icon = CategoryIcons[category] ?? Banknote;
+
+ return ;
+};
diff --git a/src/components/ui/checkbox.tsx b/src/components/ui/checkbox.tsx
new file mode 100644
index 0000000..4441c62
--- /dev/null
+++ b/src/components/ui/checkbox.tsx
@@ -0,0 +1,28 @@
+import * as React from "react"
+import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
+import { Check } from "lucide-react"
+
+import { cn } from "~/lib/utils"
+
+const Checkbox = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+
+
+))
+Checkbox.displayName = CheckboxPrimitive.Root.displayName
+
+export { Checkbox }
diff --git a/src/components/ui/command.tsx b/src/components/ui/command.tsx
new file mode 100644
index 0000000..2f9de2c
--- /dev/null
+++ b/src/components/ui/command.tsx
@@ -0,0 +1,143 @@
+import * as React from 'react';
+import { type DialogProps } from '@radix-ui/react-dialog';
+import { Command as CommandPrimitive } from 'cmdk';
+import { Search } from 'lucide-react';
+
+import { cn } from '~/lib/utils';
+import { Dialog, DialogContent } from '~/components/ui/dialog';
+
+const Command = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+Command.displayName = CommandPrimitive.displayName;
+
+type CommandDialogProps = DialogProps;
+
+const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
+ return (
+
+
+
+ {children}
+
+
+
+ );
+};
+
+const CommandInput = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+
+
+
+));
+
+CommandInput.displayName = CommandPrimitive.Input.displayName;
+
+const CommandList = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+
+CommandList.displayName = CommandPrimitive.List.displayName;
+
+const CommandEmpty = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>((props, ref) => (
+
+));
+
+CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
+
+const CommandGroup = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+
+CommandGroup.displayName = CommandPrimitive.Group.displayName;
+
+const CommandSeparator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
+
+const CommandItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+
+CommandItem.displayName = CommandPrimitive.Item.displayName;
+
+const CommandShortcut = ({ className, ...props }: React.HTMLAttributes) => {
+ return (
+
+ );
+};
+CommandShortcut.displayName = 'CommandShortcut';
+
+export {
+ Command,
+ CommandDialog,
+ CommandInput,
+ CommandList,
+ CommandEmpty,
+ CommandGroup,
+ CommandItem,
+ CommandShortcut,
+ CommandSeparator,
+};
diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx
new file mode 100644
index 0000000..48e21d5
--- /dev/null
+++ b/src/components/ui/dialog.tsx
@@ -0,0 +1,102 @@
+import * as React from 'react';
+import * as DialogPrimitive from '@radix-ui/react-dialog';
+import { X } from 'lucide-react';
+
+import { cn } from '~/lib/utils';
+
+const Dialog = DialogPrimitive.Root;
+
+const DialogTrigger = DialogPrimitive.Trigger;
+
+const DialogPortal = DialogPrimitive.Portal;
+
+const DialogClose = DialogPrimitive.Close;
+
+const DialogOverlay = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
+
+const DialogContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+ {children}
+
+
+ Close
+
+
+
+));
+DialogContent.displayName = DialogPrimitive.Content.displayName;
+
+const DialogHeader = ({ className, ...props }: React.HTMLAttributes) => (
+
+);
+DialogHeader.displayName = 'DialogHeader';
+
+const DialogFooter = ({ className, ...props }: React.HTMLAttributes) => (
+
+);
+DialogFooter.displayName = 'DialogFooter';
+
+const DialogTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+DialogTitle.displayName = DialogPrimitive.Title.displayName;
+
+const DialogDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+DialogDescription.displayName = DialogPrimitive.Description.displayName;
+
+export {
+ Dialog,
+ DialogPortal,
+ DialogOverlay,
+ DialogClose,
+ DialogTrigger,
+ DialogContent,
+ DialogHeader,
+ DialogFooter,
+ DialogTitle,
+ DialogDescription,
+};
diff --git a/src/components/ui/drawer.tsx b/src/components/ui/drawer.tsx
new file mode 100644
index 0000000..d788dfc
--- /dev/null
+++ b/src/components/ui/drawer.tsx
@@ -0,0 +1,289 @@
+import * as React from 'react';
+import { Drawer as DrawerPrimitive } from 'vaul';
+
+import { cn } from '~/lib/utils';
+import { Button } from './button';
+import useMediaQuery from '~/hooks/useMediaQuery';
+import {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+} from './dialog';
+import { useIsClient } from '~/hooks/useIsClient';
+
+const Drawer = ({
+ shouldScaleBackground = true,
+ ...props
+}: React.ComponentProps) => (
+
+);
+Drawer.displayName = 'Drawer';
+
+const DrawerTrigger = DrawerPrimitive.Trigger;
+
+const DrawerPortal = DrawerPrimitive.Portal;
+
+const DrawerClose = DrawerPrimitive.Close;
+
+const DrawerOverlay = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName;
+
+const DrawerContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, children, ...props }, ref) => (
+
+
+
+
+ {children}
+
+
+));
+DrawerContent.displayName = 'DrawerContent';
+
+const DrawerHeader = ({ className, ...props }: React.HTMLAttributes) => (
+
+);
+DrawerHeader.displayName = 'DrawerHeader';
+
+const DrawerFooter = ({ className, ...props }: React.HTMLAttributes) => (
+
+);
+DrawerFooter.displayName = 'DrawerFooter';
+
+const DrawerTitle = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+DrawerTitle.displayName = DrawerPrimitive.Title.displayName;
+
+const DrawerDescription = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+));
+DrawerDescription.displayName = DrawerPrimitive.Description.displayName;
+
+export {
+ Drawer,
+ DrawerPortal,
+ DrawerOverlay,
+ DrawerTrigger,
+ DrawerClose,
+ DrawerContent,
+ DrawerHeader,
+ DrawerFooter,
+ DrawerTitle,
+ DrawerDescription,
+};
+
+type AppDrawerProps = {
+ children: React.ReactNode;
+ trigger: React.ReactNode;
+ disableTrigger?: boolean;
+ onTriggerClick?: React.MouseEventHandler;
+ className: string;
+ open?: boolean;
+ onOpenChange?: (open: boolean) => void;
+ title?: string;
+ actionTitle?: string;
+ actionOnClick?: () => void;
+ shouldCloseOnAction?: boolean;
+ leftAction?: string;
+ leftActionOnClick?: () => void;
+ shouldCloseOnLeftAction?: boolean;
+ dismissible?: boolean;
+ actionDisabled?: boolean;
+ onClose?: () => void;
+};
+
+export const AppDrawer: React.FC = (props) => {
+ const {
+ children,
+ trigger,
+ onTriggerClick,
+ open,
+ onOpenChange,
+ title,
+ actionTitle,
+ actionOnClick,
+ shouldCloseOnAction,
+ leftAction,
+ leftActionOnClick,
+ shouldCloseOnLeftAction,
+ className,
+ dismissible,
+ actionDisabled,
+ disableTrigger,
+ onClose,
+ } = props;
+
+ const isClient = useIsClient();
+
+ const isDesktop = useMediaQuery('(min-width: 768px)');
+
+ const localOnOpenChange = (_open: boolean) => {
+ if (onOpenChange && open !== _open) onOpenChange(_open);
+ };
+
+ if (!isClient) return null;
+
+ if (isDesktop) {
+ return (
+
+ {
+ console.log('button clicked');
+ onTriggerClick?.(e);
+ }}
+ disabled={disableTrigger}
+ asChild
+ >
+ {trigger}
+
+ {
+ if (dismissible === false) {
+ e.preventDefault();
+ }
+ }}
+ >
+
+ {title}
+
+ {children}
+
+ {leftAction ? (
+
+ {shouldCloseOnLeftAction ?? shouldCloseOnLeftAction === undefined ? (
+ {leftAction}
+ ) : (
+ leftAction
+ )}
+
+ ) : null}
+ {actionTitle ? (
+ !shouldCloseOnAction ? (
+
+ {actionTitle}
+
+ ) : (
+
+ {actionTitle}
+
+ )
+ ) : (
+
+ )}
+
+
+
+ );
+ }
+ return (
+
+
+ {trigger}
+
+
+
+
+ {leftAction ? (
+
+ {shouldCloseOnLeftAction ?? shouldCloseOnLeftAction === undefined ? (
+ {leftAction}
+ ) : (
+ leftAction
+ )}
+
+ ) : (
+
+ )}
+
{title}
+ {actionTitle ? (
+ !shouldCloseOnAction ? (
+
+ {actionTitle}
+
+ ) : (
+
+ {actionTitle}
+
+ )
+ ) : (
+
+ )}
+
+
{children}
+
+
+
+ );
+};
diff --git a/src/components/ui/dual-icons.tsx b/src/components/ui/dual-icons.tsx
new file mode 100644
index 0000000..4be83eb
--- /dev/null
+++ b/src/components/ui/dual-icons.tsx
@@ -0,0 +1,24 @@
+import React from 'react';
+
+type DualIconProps = React.SVGProps & { primaryClass?: string, secondaryClass?: string };
+
+export type DualIconComponent = React.FC;
+
+export const AddSquareIcon: DualIconComponent = ({ primaryClass, secondaryClass, ...props }) => {
+ return (
+
+
+
+
+ );
+};
+
+
+export const PieChartIcon: DualIconComponent = ({ primaryClass, secondaryClass, ...props }) => {
+ return (
+
+
+
+
+ );
+};
diff --git a/src/components/ui/form.tsx b/src/components/ui/form.tsx
new file mode 100644
index 0000000..7765b62
--- /dev/null
+++ b/src/components/ui/form.tsx
@@ -0,0 +1,176 @@
+import * as React from "react"
+import * as LabelPrimitive from "@radix-ui/react-label"
+import { Slot } from "@radix-ui/react-slot"
+import {
+ Controller,
+ ControllerProps,
+ FieldPath,
+ FieldValues,
+ FormProvider,
+ useFormContext,
+} from "react-hook-form"
+
+import { cn } from "~/lib/utils"
+import { Label } from "~/components/ui/label"
+
+const Form = FormProvider
+
+type FormFieldContextValue<
+ TFieldValues extends FieldValues = FieldValues,
+ TName extends FieldPath = FieldPath
+> = {
+ name: TName
+}
+
+const FormFieldContext = React.createContext(
+ {} as FormFieldContextValue
+)
+
+const FormField = <
+ TFieldValues extends FieldValues = FieldValues,
+ TName extends FieldPath = FieldPath
+>({
+ ...props
+}: ControllerProps) => {
+ return (
+
+
+
+ )
+}
+
+const useFormField = () => {
+ const fieldContext = React.useContext(FormFieldContext)
+ const itemContext = React.useContext(FormItemContext)
+ const { getFieldState, formState } = useFormContext()
+
+ const fieldState = getFieldState(fieldContext.name, formState)
+
+ if (!fieldContext) {
+ throw new Error("useFormField should be used within ")
+ }
+
+ const { id } = itemContext
+
+ return {
+ id,
+ name: fieldContext.name,
+ formItemId: `${id}-form-item`,
+ formDescriptionId: `${id}-form-item-description`,
+ formMessageId: `${id}-form-item-message`,
+ ...fieldState,
+ }
+}
+
+type FormItemContextValue = {
+ id: string
+}
+
+const FormItemContext = React.createContext(
+ {} as FormItemContextValue
+)
+
+const FormItem = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => {
+ const id = React.useId()
+
+ return (
+
+
+
+ )
+})
+FormItem.displayName = "FormItem"
+
+const FormLabel = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => {
+ const { error, formItemId } = useFormField()
+
+ return (
+
+ )
+})
+FormLabel.displayName = "FormLabel"
+
+const FormControl = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ ...props }, ref) => {
+ const { error, formItemId, formDescriptionId, formMessageId } = useFormField()
+
+ return (
+
+ )
+})
+FormControl.displayName = "FormControl"
+
+const FormDescription = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes
+>(({ className, ...props }, ref) => {
+ const { formDescriptionId } = useFormField()
+
+ return (
+
+ )
+})
+FormDescription.displayName = "FormDescription"
+
+const FormMessage = React.forwardRef<
+ HTMLParagraphElement,
+ React.HTMLAttributes
+>(({ className, children, ...props }, ref) => {
+ const { error, formMessageId } = useFormField()
+ const body = error ? String(error?.message) : children
+
+ if (!body) {
+ return null
+ }
+
+ return (
+
+ {body}
+
+ )
+})
+FormMessage.displayName = "FormMessage"
+
+export {
+ useFormField,
+ Form,
+ FormItem,
+ FormLabel,
+ FormControl,
+ FormDescription,
+ FormMessage,
+ FormField,
+}
diff --git a/src/components/ui/glowing-stars.tsx b/src/components/ui/glowing-stars.tsx
new file mode 100644
index 0000000..fea123c
--- /dev/null
+++ b/src/components/ui/glowing-stars.tsx
@@ -0,0 +1,142 @@
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
+import React, { useEffect, useRef, useState } from 'react';
+import { AnimatePresence, motion } from 'framer-motion';
+import { cn } from '~/lib/utils';
+
+export const GlowingStarsBackgroundCard = ({
+ className,
+ children,
+}: {
+ className?: string;
+ children?: React.ReactNode;
+}) => {
+ const [mouseEnter, setMouseEnter] = useState(false);
+
+ return (
+ {
+ setMouseEnter(true);
+ }}
+ onMouseLeave={() => {
+ setMouseEnter(false);
+ }}
+ className={cn(
+ 'h-full max-h-[20rem] w-full max-w-md rounded-xl border border-[#eaeaea] bg-[linear-gradient(110deg,#333_0.6%,#222)] p-4 dark:border-neutral-600',
+ className,
+ )}
+ >
+
+
+
+
{children}
+
+ );
+};
+
+export const GlowingStarsDescription = ({
+ className,
+ children,
+}: {
+ className?: string;
+ children?: React.ReactNode;
+}) => {
+ return {children}
;
+};
+
+export const GlowingStarsTitle = ({
+ className,
+ children,
+}: {
+ className?: string;
+ children?: React.ReactNode;
+}) => {
+ return {children} ;
+};
+
+export const Illustration = ({ mouseEnter }: { mouseEnter: boolean }) => {
+ const stars = 108;
+ const columns = 18;
+
+ const [glowingStars, setGlowingStars] = useState([]);
+
+ const highlightedStars = useRef([]);
+
+ useEffect(() => {
+ const interval = setInterval(() => {
+ highlightedStars.current = Array.from({ length: 5 }, () => Math.floor(Math.random() * stars));
+ setGlowingStars([...highlightedStars.current]);
+ }, 3000);
+
+ return () => clearInterval(interval);
+ }, []);
+
+ return (
+
+ {[...Array(stars)].map((_, starIdx) => {
+ const isGlowing = glowingStars.includes(starIdx);
+ const delay = (starIdx % 10) * 0.1;
+ const staticDelay = starIdx * 0.01;
+ return (
+
+
+ {mouseEnter &&
}
+
{isGlowing && }
+
+ );
+ })}
+
+ );
+};
+
+const Star = ({ isGlowing, delay }: { isGlowing: boolean; delay: number }) => {
+ return (
+
+ );
+};
+
+const Glow = ({ delay }: { delay: number }) => {
+ return (
+
+ );
+};
diff --git a/src/components/ui/input-otp.tsx b/src/components/ui/input-otp.tsx
new file mode 100644
index 0000000..c90d6a9
--- /dev/null
+++ b/src/components/ui/input-otp.tsx
@@ -0,0 +1,75 @@
+import * as React from 'react';
+import { OTPInput, OTPInputContext } from 'input-otp';
+import { Dot } from 'lucide-react';
+
+import { cn } from '~/lib/utils';
+
+const InputOTP = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, containerClassName, ...props }, ref) => (
+
+));
+InputOTP.displayName = 'InputOTP';
+
+const InputOTPGroup = React.forwardRef<
+ React.ElementRef<'div'>,
+ React.ComponentPropsWithoutRef<'div'>
+>(({ className, ...props }, ref) => (
+
+));
+InputOTPGroup.displayName = 'InputOTPGroup';
+
+const InputOTPSlot = React.forwardRef<
+ React.ElementRef<'div'>,
+ React.ComponentPropsWithoutRef<'div'> & { index: number | string }
+>(({ index, className, ...props }, ref) => {
+ const inputOTPContext = React.useContext(OTPInputContext);
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
+ const { char, hasFakeCaret, isActive } = inputOTPContext.slots[Number(index)] ?? {
+ char: '',
+ hasFakeCaret: false,
+ isActive: false,
+ };
+
+ return (
+
+ {char}
+ {hasFakeCaret && (
+
+ )}
+
+ );
+});
+InputOTPSlot.displayName = 'InputOTPSlot';
+
+const InputOTPSeparator = React.forwardRef<
+ React.ElementRef<'div'>,
+ React.ComponentPropsWithoutRef<'div'>
+>(({ ...props }, ref) => (
+
+
+
+));
+InputOTPSeparator.displayName = 'InputOTPSeparator';
+
+export { InputOTP, InputOTPGroup, InputOTPSlot, InputOTPSeparator };
diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx
new file mode 100644
index 0000000..473b587
--- /dev/null
+++ b/src/components/ui/input.tsx
@@ -0,0 +1,24 @@
+import * as React from 'react';
+
+import { cn } from '~/lib/utils';
+
+export type InputProps = React.InputHTMLAttributes;
+
+const Input = React.forwardRef(
+ ({ className, type, ...props }, ref) => {
+ return (
+
+ );
+ },
+);
+Input.displayName = 'Input';
+
+export { Input };
diff --git a/src/components/ui/label.tsx b/src/components/ui/label.tsx
new file mode 100644
index 0000000..b172859
--- /dev/null
+++ b/src/components/ui/label.tsx
@@ -0,0 +1,24 @@
+import * as React from "react"
+import * as LabelPrimitive from "@radix-ui/react-label"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "~/lib/utils"
+
+const labelVariants = cva(
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
+)
+
+const Label = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef &
+ VariantProps
+>(({ className, ...props }, ref) => (
+
+))
+Label.displayName = LabelPrimitive.Root.displayName
+
+export { Label }
diff --git a/src/components/ui/popover.tsx b/src/components/ui/popover.tsx
new file mode 100644
index 0000000..390ec69
--- /dev/null
+++ b/src/components/ui/popover.tsx
@@ -0,0 +1,29 @@
+import * as React from "react"
+import * as PopoverPrimitive from "@radix-ui/react-popover"
+
+import { cn } from "~/lib/utils"
+
+const Popover = PopoverPrimitive.Root
+
+const PopoverTrigger = PopoverPrimitive.Trigger
+
+const PopoverContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
+
+
+
+))
+PopoverContent.displayName = PopoverPrimitive.Content.displayName
+
+export { Popover, PopoverTrigger, PopoverContent }
diff --git a/src/components/ui/separator.tsx b/src/components/ui/separator.tsx
new file mode 100644
index 0000000..b35cbc8
--- /dev/null
+++ b/src/components/ui/separator.tsx
@@ -0,0 +1,29 @@
+import * as React from "react"
+import * as SeparatorPrimitive from "@radix-ui/react-separator"
+
+import { cn } from "~/lib/utils"
+
+const Separator = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(
+ (
+ { className, orientation = "horizontal", decorative = true, ...props },
+ ref
+ ) => (
+
+ )
+)
+Separator.displayName = SeparatorPrimitive.Root.displayName
+
+export { Separator }
diff --git a/src/components/ui/skeleton.tsx b/src/components/ui/skeleton.tsx
new file mode 100644
index 0000000..89c265b
--- /dev/null
+++ b/src/components/ui/skeleton.tsx
@@ -0,0 +1,18 @@
+import { cn } from '~/lib/utils';
+
+function Skeleton({ className, ...props }: React.HTMLAttributes) {
+ return
;
+}
+
+const BalanceSkeleton: React.FC = () => {
+ return (
+
+ );
+};
+
+export { Skeleton, BalanceSkeleton };
diff --git a/src/components/ui/sonner.tsx b/src/components/ui/sonner.tsx
new file mode 100644
index 0000000..435450c
--- /dev/null
+++ b/src/components/ui/sonner.tsx
@@ -0,0 +1,29 @@
+import { useTheme } from "next-themes"
+import { Toaster as Sonner } from "sonner"
+
+type ToasterProps = React.ComponentProps
+
+const Toaster = ({ ...props }: ToasterProps) => {
+ const { theme = "system" } = useTheme()
+
+ return (
+
+ )
+}
+
+export { Toaster }
diff --git a/src/components/ui/spinner.tsx b/src/components/ui/spinner.tsx
new file mode 100644
index 0000000..087bfb7
--- /dev/null
+++ b/src/components/ui/spinner.tsx
@@ -0,0 +1,24 @@
+import { cn } from '~/lib/utils';
+
+export const LoadingSpinner: React.FC> = ({
+ className,
+ ...props
+}) => {
+ return (
+
+
+
+ );
+};
diff --git a/src/components/ui/tabs.tsx b/src/components/ui/tabs.tsx
new file mode 100644
index 0000000..651a4c4
--- /dev/null
+++ b/src/components/ui/tabs.tsx
@@ -0,0 +1,53 @@
+import * as React from "react"
+import * as TabsPrimitive from "@radix-ui/react-tabs"
+
+import { cn } from "~/lib/utils"
+
+const Tabs = TabsPrimitive.Root
+
+const TabsList = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+TabsList.displayName = TabsPrimitive.List.displayName
+
+const TabsTrigger = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
+
+const TabsContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => (
+
+))
+TabsContent.displayName = TabsPrimitive.Content.displayName
+
+export { Tabs, TabsList, TabsTrigger, TabsContent }
diff --git a/src/components/ui/textarea.tsx b/src/components/ui/textarea.tsx
new file mode 100644
index 0000000..75e8b33
--- /dev/null
+++ b/src/components/ui/textarea.tsx
@@ -0,0 +1,23 @@
+import * as React from 'react';
+
+import { cn } from '~/lib/utils';
+
+export type TextareaProps = React.TextareaHTMLAttributes;
+
+const Textarea = React.forwardRef(
+ ({ className, ...props }, ref) => {
+ return (
+
+ );
+ },
+);
+Textarea.displayName = 'Textarea';
+
+export { Textarea };
diff --git a/src/env.js b/src/env.js
new file mode 100644
index 0000000..085c4be
--- /dev/null
+++ b/src/env.js
@@ -0,0 +1,106 @@
+import { createEnv } from '@t3-oss/env-nextjs';
+import { z } from 'zod';
+
+export const env = createEnv({
+ /**
+ * Specify your server-side environment variables schema here. This way you can ensure the app
+ * isn't built with invalid env vars.
+ */
+ server: {
+ DATABASE_URL: z
+ .string()
+ .url()
+ .refine(
+ (str) => !str.includes('YOUR_MYSQL_URL_HERE'),
+ 'You forgot to change the default URL',
+ ),
+ NODE_ENV: z.enum(['development', 'test', 'production']).default('development'),
+ NEXTAUTH_SECRET: process.env.NODE_ENV === 'production' ? z.string() : z.string().optional(),
+ NEXTAUTH_URL: z.preprocess(
+ // This makes Vercel deployments not fail if you don't set NEXTAUTH_URL
+ // Since NextAuth.js automatically uses the VERCEL_URL if present.
+ (str) => process.env.VERCEL_URL ?? str,
+ // VERCEL_URL doesn't include `https` so it cant be validated as a URL
+ process.env.VERCEL ? z.string() : z.string().url(),
+ ),
+ NEXTAUTH_URL_INTERNAL: z.preprocess(
+ (str) => process.env.VERCEL_URL ?? str,
+ process.env.VERCEL ? z.string() : z.string().url(),
+ ),
+ ENABLE_SENDING_INVITES: z.boolean(),
+ INVITE_ONLY: z.boolean(),
+ FROM_EMAIL: z.string().optional(),
+ EMAIL_SERVER_HOST: z.string().optional(),
+ EMAIL_SERVER_PORT: z.string().optional(),
+ EMAIL_SERVER_USER: z.string().optional(),
+ EMAIL_SERVER_PASSWORD: z.string().optional(),
+ GOOGLE_CLIENT_ID: z.string().optional(),
+ GOOGLE_CLIENT_SECRET: z.string().optional(),
+ AUTHENTIK_ID: z.string().optional(),
+ AUTHENTIK_SECRET: z.string().optional(),
+ AUTHENTIK_ISSUER: z.string().optional(),
+ R2_ACCESS_KEY: z.string().optional(),
+ R2_SECRET_KEY: z.string().optional(),
+ R2_BUCKET: z.string().optional(),
+ R2_URL: z.string().optional(),
+ R2_PUBLIC_URL: z.string().optional(),
+ WEB_PUSH_EMAIL: z.string().optional(),
+ WEB_PUSH_PRIVATE_KEY: z.string().optional(),
+ WEB_PUSH_PUBLIC_KEY: z.string().optional(),
+ FEEDBACK_EMAIL: z.string().optional(),
+ DISCORD_WEBHOOK_URL: z.string().optional(),
+ },
+
+ /**
+ * Specify your client-side environment variables schema here. This way you can ensure the app
+ * isn't built with invalid env vars. To expose them to the client, prefix them with
+ * `NEXT_PUBLIC_`.
+ */
+ client: {},
+
+ /**
+ * You can't destruct `process.env` as a regular object in the Next.js edge runtimes (e.g.
+ * middlewares) or client-side so we need to destruct manually.
+ */
+ runtimeEnv: {
+ DATABASE_URL:
+ process.env.DATABASE_URL ??
+ `postgresql://${process.env.POSTGRES_USER}:${process.env.POSTGRES_PASSWORD}@${process.env.POSTGRES_HOST}:${process.env.POSTGRES_PORT}`,
+ NODE_ENV: process.env.NODE_ENV,
+ NEXTAUTH_SECRET: process.env.NEXTAUTH_SECRET,
+ NEXTAUTH_URL: process.env.NEXTAUTH_URL,
+ NEXTAUTH_URL_INTERNAL: process.env.NEXTAUTH_URL_INTERNAL ?? process.env.NEXTAUTH_URL,
+ ENABLE_SENDING_INVITES: process.env.ENABLE_SENDING_INVITES === 'true',
+ INVITE_ONLY: process.env.INVITE_ONLY === 'true',
+ FROM_EMAIL: process.env.FROM_EMAIL,
+ EMAIL_SERVER_HOST: process.env.EMAIL_SERVER_HOST,
+ EMAIL_SERVER_PORT: process.env.EMAIL_SERVER_PORT,
+ EMAIL_SERVER_USER: process.env.EMAIL_SERVER_USER,
+ EMAIL_SERVER_PASSWORD: process.env.EMAIL_SERVER_PASSWORD,
+ GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID,
+ GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET,
+ AUTHENTIK_ID: process.env.AUTHENTIK_ID,
+ AUTHENTIK_SECRET: process.env.AUTHENTIK_SECRET,
+ AUTHENTIK_ISSUER: process.env.AUTHENTIK_ISSUER,
+ R2_ACCESS_KEY: process.env.R2_ACCESS_KEY,
+ R2_SECRET_KEY: process.env.R2_SECRET_KEY,
+ R2_BUCKET: process.env.R2_BUCKET,
+ R2_URL: process.env.R2_URL,
+ R2_PUBLIC_URL: process.env.R2_PUBLIC_URL,
+ WEB_PUSH_EMAIL: process.env.WEB_PUSH_EMAIL,
+ WEB_PUSH_PRIVATE_KEY: process.env.WEB_PUSH_PRIVATE_KEY,
+ WEB_PUSH_PUBLIC_KEY: process.env.WEB_PUSH_PUBLIC_KEY,
+ FEEDBACK_EMAIL: process.env.FEEDBACK_EMAIL,
+ DISCORD_WEBHOOK_URL: process.env.DISCORD_WEBHOOK_URL,
+ },
+ /**
+ * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially
+ * useful for Docker builds.
+ */
+ skipValidation: !!process.env.SKIP_ENV_VALIDATION,
+ /**
+ * Makes it so that empty strings are treated as undefined. `SOME_VAR: z.string()` and
+ * `SOME_VAR=''` will throw an error.
+ */
+ emptyStringAsUndefined: true,
+});
diff --git a/src/hooks/useEnableAfter.ts b/src/hooks/useEnableAfter.ts
new file mode 100644
index 0000000..247cb4f
--- /dev/null
+++ b/src/hooks/useEnableAfter.ts
@@ -0,0 +1,22 @@
+import { useEffect, useState } from 'react';
+
+/**
+ * A React hook that returns true after a specified delay.
+ * @param delay The delay in milliseconds after which to return true.
+ * @returns A boolean value that becomes true after the specified delay.
+ */
+const useEnableAfter = (delay: number): boolean => {
+ const [isEnabled, setIsEnabled] = useState(false);
+
+ useEffect(() => {
+ const timer = setTimeout(() => {
+ setIsEnabled(true);
+ }, delay);
+
+ return () => clearTimeout(timer);
+ }, [delay]);
+
+ return isEnabled;
+};
+
+export default useEnableAfter;
diff --git a/src/hooks/useIsClient.ts b/src/hooks/useIsClient.ts
new file mode 100644
index 0000000..de7e071
--- /dev/null
+++ b/src/hooks/useIsClient.ts
@@ -0,0 +1,11 @@
+import { useCallback, useEffect, useRef, useState } from 'react';
+
+export const useIsClient = () => {
+ const [isClient, setIsClient] = useState(false);
+
+ useEffect(() => {
+ setIsClient(true);
+ }, []);
+
+ return isClient;
+};
diff --git a/src/hooks/useMediaQuery.ts b/src/hooks/useMediaQuery.ts
new file mode 100644
index 0000000..d27c265
--- /dev/null
+++ b/src/hooks/useMediaQuery.ts
@@ -0,0 +1,36 @@
+import { useEffect, useState } from 'react';
+
+/**
+ * A React hook that listens for changes in a media query
+ * @param query - The media query to listen for
+ * @returns A boolean indicating if the query matches
+ */
+const useMediaQuery = (query: string): boolean => {
+ const getMatches = (query: string): boolean => {
+ // Prevents SSR issues
+ if (typeof window !== 'undefined') {
+ return window.matchMedia(query).matches;
+ }
+ return false;
+ };
+
+ const [matches, setMatches] = useState(getMatches(query));
+
+ useEffect(() => {
+ const mediaQueryList = window.matchMedia(query);
+ const documentChangeHandler = () => setMatches(mediaQueryList.matches);
+
+ // Add listener
+ mediaQueryList.addEventListener('change', documentChangeHandler);
+
+ // Call handler right away so state gets updated with initial value
+ documentChangeHandler();
+
+ // Remove listener on cleanup
+ return () => mediaQueryList.removeEventListener('change', documentChangeHandler);
+ }, [query]);
+
+ return matches;
+};
+
+export default useMediaQuery;
diff --git a/src/instrumentation.ts b/src/instrumentation.ts
new file mode 100644
index 0000000..256ea96
--- /dev/null
+++ b/src/instrumentation.ts
@@ -0,0 +1,12 @@
+/**
+ * Add things here to be executed during server startup.
+ *
+ * more details here: https://nextjs.org/docs/app/building-your-application/optimizing/instrumentation
+ */
+export async function register() {
+ if (process.env.NEXT_RUNTIME === 'nodejs') {
+ console.log('Registering instrumentation');
+ const { validateAuthEnv } = await import('./server/auth');
+ validateAuthEnv();
+ }
+}
diff --git a/src/lib/constants.ts b/src/lib/constants.ts
new file mode 100644
index 0000000..1b106c7
--- /dev/null
+++ b/src/lib/constants.ts
@@ -0,0 +1 @@
+export const FILE_SIZE_LIMIT = 5 * 1024 * 1024;
diff --git a/src/lib/currency.ts b/src/lib/currency.ts
new file mode 100644
index 0000000..84d2694
--- /dev/null
+++ b/src/lib/currency.ts
@@ -0,0 +1,1073 @@
+export const CURRENCIES = [
+ {
+ name: 'US Dollar',
+ symbol: '$',
+ symbolNative: '$',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'USD',
+ namePlural: 'US dollars',
+ },
+ {
+ name: 'Canadian Dollar',
+ symbol: 'CA$',
+ symbolNative: '$',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'CAD',
+ namePlural: 'Canadian dollars',
+ },
+ {
+ name: 'Euro',
+ symbol: '€',
+ symbolNative: '€',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'EUR',
+ namePlural: 'euros',
+ },
+ {
+ name: 'United Arab Emirates Dirham',
+ symbol: 'AED',
+ symbolNative: 'د.إ.',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'AED',
+ namePlural: 'UAE dirhams',
+ },
+ {
+ name: 'Afghan Afghani',
+ symbol: 'Af',
+ symbolNative: '؋',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'AFN',
+ namePlural: 'Afghan Afghanis',
+ },
+ {
+ name: 'Albanian Lek',
+ symbol: 'ALL',
+ symbolNative: 'Lek',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'ALL',
+ namePlural: 'Albanian lekë',
+ },
+ {
+ name: 'Armenian Dram',
+ symbol: 'AMD',
+ symbolNative: 'դր.',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'AMD',
+ namePlural: 'Armenian drams',
+ },
+ {
+ name: 'Argentine Peso',
+ symbol: 'AR$',
+ symbolNative: '$',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'ARS',
+ namePlural: 'Argentine pesos',
+ },
+ {
+ name: 'Australian Dollar',
+ symbol: 'AU$',
+ symbolNative: '$',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'AUD',
+ namePlural: 'Australian dollars',
+ },
+ {
+ name: 'Azerbaijani Manat',
+ symbol: 'man.',
+ symbolNative: 'ман.',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'AZN',
+ namePlural: 'Azerbaijani manats',
+ },
+ {
+ name: 'Bosnia-Herzegovina Convertible Mark',
+ symbol: 'KM',
+ symbolNative: 'KM',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'BAM',
+ namePlural: 'Bosnia-Herzegovina convertible marks',
+ },
+ {
+ name: 'Bangladeshi Taka',
+ symbol: 'Tk',
+ symbolNative: '৳',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'BDT',
+ namePlural: 'Bangladeshi takas',
+ },
+ {
+ name: 'Bulgarian Lev',
+ symbol: 'BGN',
+ symbolNative: 'лв.',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'BGN',
+ namePlural: 'Bulgarian leva',
+ },
+ {
+ name: 'Bahraini Dinar',
+ symbol: 'BD',
+ symbolNative: 'د.ب.',
+ decimalDigits: 3,
+ rounding: 0,
+ code: 'BHD',
+ namePlural: 'Bahraini dinars',
+ },
+ {
+ name: 'Burundian Franc',
+ symbol: 'FBu',
+ symbolNative: 'FBu',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'BIF',
+ namePlural: 'Burundian francs',
+ },
+ {
+ name: 'Brunei Dollar',
+ symbol: 'BN$',
+ symbolNative: '$',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'BND',
+ namePlural: 'Brunei dollars',
+ },
+ {
+ name: 'Bolivian Boliviano',
+ symbol: 'Bs',
+ symbolNative: 'Bs',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'BOB',
+ namePlural: 'Bolivian bolivianos',
+ },
+ {
+ name: 'Brazilian Real',
+ symbol: 'R$',
+ symbolNative: 'R$',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'BRL',
+ namePlural: 'Brazilian reals',
+ },
+ {
+ name: 'Botswanan Pula',
+ symbol: 'BWP',
+ symbolNative: 'P',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'BWP',
+ namePlural: 'Botswanan pulas',
+ },
+ {
+ name: 'Belarusian Ruble',
+ symbol: 'Br',
+ symbolNative: 'руб.',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'BYN',
+ namePlural: 'Belarusian rubles',
+ },
+ {
+ name: 'Belize Dollar',
+ symbol: 'BZ$',
+ symbolNative: '$',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'BZD',
+ namePlural: 'Belize dollars',
+ },
+ {
+ name: 'Congolese Franc',
+ symbol: 'CDF',
+ symbolNative: 'FrCD',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'CDF',
+ namePlural: 'Congolese francs',
+ },
+ {
+ name: 'Swiss Franc',
+ symbol: 'CHF',
+ symbolNative: 'CHF',
+ decimalDigits: 2,
+ rounding: 0.05,
+ code: 'CHF',
+ namePlural: 'Swiss francs',
+ },
+ {
+ name: 'Chilean Peso',
+ symbol: 'CL$',
+ symbolNative: '$',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'CLP',
+ namePlural: 'Chilean pesos',
+ },
+ {
+ name: 'Chinese Yuan',
+ symbol: 'CN¥',
+ symbolNative: 'CN¥',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'CNY',
+ namePlural: 'Chinese yuan',
+ },
+ {
+ name: 'Colombian Peso',
+ symbol: 'CO$',
+ symbolNative: '$',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'COP',
+ namePlural: 'Colombian pesos',
+ },
+ {
+ name: 'Costa Rican Colón',
+ symbol: '₡',
+ symbolNative: '₡',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'CRC',
+ namePlural: 'Costa Rican colóns',
+ },
+ {
+ name: 'Cape Verdean Escudo',
+ symbol: 'CV$',
+ symbolNative: 'CV$',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'CVE',
+ namePlural: 'Cape Verdean escudos',
+ },
+ {
+ name: 'Czech Republic Koruna',
+ symbol: 'Kč',
+ symbolNative: 'Kč',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'CZK',
+ namePlural: 'Czech Republic korunas',
+ },
+ {
+ name: 'Djiboutian Franc',
+ symbol: 'Fdj',
+ symbolNative: 'Fdj',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'DJF',
+ namePlural: 'Djiboutian francs',
+ },
+ {
+ name: 'Danish Krone',
+ symbol: 'Dkr',
+ symbolNative: 'kr',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'DKK',
+ namePlural: 'Danish kroner',
+ },
+ {
+ name: 'Dominican Peso',
+ symbol: 'RD$',
+ symbolNative: 'RD$',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'DOP',
+ namePlural: 'Dominican pesos',
+ },
+ {
+ name: 'Algerian Dinar',
+ symbol: 'DA',
+ symbolNative: 'د.ج.',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'DZD',
+ namePlural: 'Algerian dinars',
+ },
+ {
+ name: 'Estonian Kroon',
+ symbol: 'Ekr',
+ symbolNative: 'kr',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'EEK',
+ namePlural: 'Estonian kroons',
+ },
+ {
+ name: 'Egyptian Pound',
+ symbol: 'EGP',
+ symbolNative: 'ج.م.',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'EGP',
+ namePlural: 'Egyptian pounds',
+ },
+ {
+ name: 'Eritrean Nakfa',
+ symbol: 'Nfk',
+ symbolNative: 'Nfk',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'ERN',
+ namePlural: 'Eritrean nakfas',
+ },
+ {
+ name: 'Ethiopian Birr',
+ symbol: 'Br',
+ symbolNative: 'Br',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'ETB',
+ namePlural: 'Ethiopian birrs',
+ },
+ {
+ name: 'British Pound Sterling',
+ symbol: '£',
+ symbolNative: '£',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'GBP',
+ namePlural: 'British pounds sterling',
+ },
+ {
+ name: 'Georgian Lari',
+ symbol: 'GEL',
+ symbolNative: 'GEL',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'GEL',
+ namePlural: 'Georgian laris',
+ },
+ {
+ name: 'Ghanaian Cedi',
+ symbol: 'GH₵',
+ symbolNative: 'GH₵',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'GHS',
+ namePlural: 'Ghanaian cedis',
+ },
+ {
+ name: 'Guinean Franc',
+ symbol: 'FG',
+ symbolNative: 'FG',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'GNF',
+ namePlural: 'Guinean francs',
+ },
+ {
+ name: 'Guatemalan Quetzal',
+ symbol: 'GTQ',
+ symbolNative: 'Q',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'GTQ',
+ namePlural: 'Guatemalan quetzals',
+ },
+ {
+ name: 'Hong Kong Dollar',
+ symbol: 'HK$',
+ symbolNative: '$',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'HKD',
+ namePlural: 'Hong Kong dollars',
+ },
+ {
+ name: 'Honduran Lempira',
+ symbol: 'HNL',
+ symbolNative: 'L',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'HNL',
+ namePlural: 'Honduran lempiras',
+ },
+ {
+ name: 'Croatian Kuna',
+ symbol: 'kn',
+ symbolNative: 'kn',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'HRK',
+ namePlural: 'Croatian kunas',
+ },
+ {
+ name: 'Hungarian Forint',
+ symbol: 'Ft',
+ symbolNative: 'Ft',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'HUF',
+ namePlural: 'Hungarian forints',
+ },
+ {
+ name: 'Indonesian Rupiah',
+ symbol: 'Rp',
+ symbolNative: 'Rp',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'IDR',
+ namePlural: 'Indonesian rupiahs',
+ },
+ {
+ name: 'Israeli New Sheqel',
+ symbol: '₪',
+ symbolNative: '₪',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'ILS',
+ namePlural: 'Israeli new sheqels',
+ },
+ {
+ name: 'Indian Rupee',
+ symbol: '₹',
+ symbolNative: '₹',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'INR',
+ namePlural: 'Indian rupees',
+ },
+ {
+ name: 'Iraqi Dinar',
+ symbol: 'IQD',
+ symbolNative: 'د.ع.',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'IQD',
+ namePlural: 'Iraqi dinars',
+ },
+ {
+ name: 'Iranian Rial',
+ symbol: 'IRR',
+ symbolNative: '﷼',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'IRR',
+ namePlural: 'Iranian rials',
+ },
+ {
+ name: 'Icelandic Króna',
+ symbol: 'Ikr',
+ symbolNative: 'kr',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'ISK',
+ namePlural: 'Icelandic krónur',
+ },
+ {
+ name: 'Jamaican Dollar',
+ symbol: 'J$',
+ symbolNative: '$',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'JMD',
+ namePlural: 'Jamaican dollars',
+ },
+ {
+ name: 'Jordanian Dinar',
+ symbol: 'JD',
+ symbolNative: 'د.أ.',
+ decimalDigits: 3,
+ rounding: 0,
+ code: 'JOD',
+ namePlural: 'Jordanian dinars',
+ },
+ {
+ name: 'Japanese Yen',
+ symbol: '¥',
+ symbolNative: '¥',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'JPY',
+ namePlural: 'Japanese yen',
+ },
+ {
+ name: 'Kenyan Shilling',
+ symbol: 'Ksh',
+ symbolNative: 'Ksh',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'KES',
+ namePlural: 'Kenyan shillings',
+ },
+ {
+ name: 'Cambodian Riel',
+ symbol: 'KHR',
+ symbolNative: '៛',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'KHR',
+ namePlural: 'Cambodian riels',
+ },
+ {
+ name: 'Comorian Franc',
+ symbol: 'CF',
+ symbolNative: 'FC',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'KMF',
+ namePlural: 'Comorian francs',
+ },
+ {
+ name: 'South Korean Won',
+ symbol: '₩',
+ symbolNative: '₩',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'KRW',
+ namePlural: 'South Korean won',
+ },
+ {
+ name: 'Kuwaiti Dinar',
+ symbol: 'KD',
+ symbolNative: 'د.ك.',
+ decimalDigits: 3,
+ rounding: 0,
+ code: 'KWD',
+ namePlural: 'Kuwaiti dinars',
+ },
+ {
+ name: 'Kazakhstani Tenge',
+ symbol: 'KZT',
+ symbolNative: 'тңг.',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'KZT',
+ namePlural: 'Kazakhstani tenges',
+ },
+ {
+ name: 'Lebanese Pound',
+ symbol: 'LB£',
+ symbolNative: 'ل.ل.',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'LBP',
+ namePlural: 'Lebanese pounds',
+ },
+ {
+ name: 'Sri Lankan Rupee',
+ symbol: 'SLRs',
+ symbolNative: 'SL Re',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'LKR',
+ namePlural: 'Sri Lankan rupees',
+ },
+ {
+ name: 'Lithuanian Litas',
+ symbol: 'Lt',
+ symbolNative: 'Lt',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'LTL',
+ namePlural: 'Lithuanian litai',
+ },
+ {
+ name: 'Latvian Lats',
+ symbol: 'Ls',
+ symbolNative: 'Ls',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'LVL',
+ namePlural: 'Latvian lati',
+ },
+ {
+ name: 'Libyan Dinar',
+ symbol: 'LD',
+ symbolNative: 'د.ل.',
+ decimalDigits: 3,
+ rounding: 0,
+ code: 'LYD',
+ namePlural: 'Libyan dinars',
+ },
+ {
+ name: 'Moroccan Dirham',
+ symbol: 'MAD',
+ symbolNative: 'د.م.',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'MAD',
+ namePlural: 'Moroccan dirhams',
+ },
+ {
+ name: 'Moldovan Leu',
+ symbol: 'MDL',
+ symbolNative: 'MDL',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'MDL',
+ namePlural: 'Moldovan lei',
+ },
+ {
+ name: 'Malagasy Ariary',
+ symbol: 'MGA',
+ symbolNative: 'MGA',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'MGA',
+ namePlural: 'Malagasy Ariaries',
+ },
+ {
+ name: 'Macedonian Denar',
+ symbol: 'MKD',
+ symbolNative: 'MKD',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'MKD',
+ namePlural: 'Macedonian denari',
+ },
+ {
+ name: 'Myanma Kyat',
+ symbol: 'MMK',
+ symbolNative: 'K',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'MMK',
+ namePlural: 'Myanma kyats',
+ },
+ {
+ name: 'Macanese Pataca',
+ symbol: 'MOP$',
+ symbolNative: 'MOP$',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'MOP',
+ namePlural: 'Macanese patacas',
+ },
+ {
+ name: 'Mauritian Rupee',
+ symbol: 'MURs',
+ symbolNative: 'MURs',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'MUR',
+ namePlural: 'Mauritian rupees',
+ },
+ {
+ name: 'Mexican Peso',
+ symbol: 'MX$',
+ symbolNative: '$',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'MXN',
+ namePlural: 'Mexican pesos',
+ },
+ {
+ name: 'Malaysian Ringgit',
+ symbol: 'RM',
+ symbolNative: 'RM',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'MYR',
+ namePlural: 'Malaysian ringgits',
+ },
+ {
+ name: 'Mozambican Metical',
+ symbol: 'MTn',
+ symbolNative: 'MTn',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'MZN',
+ namePlural: 'Mozambican meticals',
+ },
+ {
+ name: 'Namibian Dollar',
+ symbol: 'N$',
+ symbolNative: 'N$',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'NAD',
+ namePlural: 'Namibian dollars',
+ },
+ {
+ name: 'Nigerian Naira',
+ symbol: '₦',
+ symbolNative: '₦',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'NGN',
+ namePlural: 'Nigerian nairas',
+ },
+ {
+ name: 'Nicaraguan Córdoba',
+ symbol: 'C$',
+ symbolNative: 'C$',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'NIO',
+ namePlural: 'Nicaraguan córdobas',
+ },
+ {
+ name: 'Norwegian Krone',
+ symbol: 'Nkr',
+ symbolNative: 'kr',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'NOK',
+ namePlural: 'Norwegian kroner',
+ },
+ {
+ name: 'Nepalese Rupee',
+ symbol: 'NPRs',
+ symbolNative: 'नेरू',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'NPR',
+ namePlural: 'Nepalese rupees',
+ },
+ {
+ name: 'New Zealand Dollar',
+ symbol: 'NZ$',
+ symbolNative: '$',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'NZD',
+ namePlural: 'New Zealand dollars',
+ },
+ {
+ name: 'Omani Rial',
+ symbol: 'OMR',
+ symbolNative: 'ر.ع.',
+ decimalDigits: 3,
+ rounding: 0,
+ code: 'OMR',
+ namePlural: 'Omani rials',
+ },
+ {
+ name: 'Panamanian Balboa',
+ symbol: 'B/.',
+ symbolNative: 'B/.',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'PAB',
+ namePlural: 'Panamanian balboas',
+ },
+ {
+ name: 'Peruvian Nuevo Sol',
+ symbol: 'S/.',
+ symbolNative: 'S/.',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'PEN',
+ namePlural: 'Peruvian nuevos soles',
+ },
+ {
+ name: 'Philippine Peso',
+ symbol: '₱',
+ symbolNative: '₱',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'PHP',
+ namePlural: 'Philippine pesos',
+ },
+ {
+ name: 'Pakistani Rupee',
+ symbol: 'PKRs',
+ symbolNative: '₨',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'PKR',
+ namePlural: 'Pakistani rupees',
+ },
+ {
+ name: 'Polish Zloty',
+ symbol: 'zł',
+ symbolNative: 'zł',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'PLN',
+ namePlural: 'Polish zlotys',
+ },
+ {
+ name: 'Paraguayan Guarani',
+ symbol: '₲',
+ symbolNative: '₲',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'PYG',
+ namePlural: 'Paraguayan guaranis',
+ },
+ {
+ name: 'Qatari Rial',
+ symbol: 'QR',
+ symbolNative: 'ر.ق.',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'QAR',
+ namePlural: 'Qatari rials',
+ },
+ {
+ name: 'Romanian Leu',
+ symbol: 'RON',
+ symbolNative: 'RON',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'RON',
+ namePlural: 'Romanian lei',
+ },
+ {
+ name: 'Serbian Dinar',
+ symbol: 'din.',
+ symbolNative: 'дин.',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'RSD',
+ namePlural: 'Serbian dinars',
+ },
+ {
+ name: 'Russian Ruble',
+ symbol: 'RUB',
+ symbolNative: '₽.',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'RUB',
+ namePlural: 'Russian rubles',
+ },
+ {
+ name: 'Rwandan Franc',
+ symbol: 'RWF',
+ symbolNative: 'FR',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'RWF',
+ namePlural: 'Rwandan francs',
+ },
+ {
+ name: 'Saudi Riyal',
+ symbol: 'SR',
+ symbolNative: 'ر.س.',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'SAR',
+ namePlural: 'Saudi riyals',
+ },
+ {
+ name: 'Sudanese Pound',
+ symbol: 'SDG',
+ symbolNative: 'SDG',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'SDG',
+ namePlural: 'Sudanese pounds',
+ },
+ {
+ name: 'Swedish Krona',
+ symbol: 'Skr',
+ symbolNative: 'kr',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'SEK',
+ namePlural: 'Swedish kronor',
+ },
+ {
+ name: 'Singapore Dollar',
+ symbol: 'S$',
+ symbolNative: '$',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'SGD',
+ namePlural: 'Singapore dollars',
+ },
+ {
+ name: 'Somali Shilling',
+ symbol: 'Ssh',
+ symbolNative: 'Ssh',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'SOS',
+ namePlural: 'Somali shillings',
+ },
+ {
+ name: 'Syrian Pound',
+ symbol: 'SY£',
+ symbolNative: 'ل.س.',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'SYP',
+ namePlural: 'Syrian pounds',
+ },
+ {
+ name: 'Thai Baht',
+ symbol: '฿',
+ symbolNative: '฿',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'THB',
+ namePlural: 'Thai baht',
+ },
+ {
+ name: 'Tunisian Dinar',
+ symbol: 'DT',
+ symbolNative: 'د.ت.',
+ decimalDigits: 3,
+ rounding: 0,
+ code: 'TND',
+ namePlural: 'Tunisian dinars',
+ },
+ {
+ name: 'Tongan Paʻanga',
+ symbol: 'T$',
+ symbolNative: 'T$',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'TOP',
+ namePlural: 'Tongan paʻanga',
+ },
+ {
+ name: 'Turkish Lira',
+ symbol: 'TL',
+ symbolNative: 'TL',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'TRY',
+ namePlural: 'Turkish Lira',
+ },
+ {
+ name: 'Trinidad and Tobago Dollar',
+ symbol: 'TT$',
+ symbolNative: '$',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'TTD',
+ namePlural: 'Trinidad and Tobago dollars',
+ },
+ {
+ name: 'New Taiwan Dollar',
+ symbol: 'NT$',
+ symbolNative: 'NT$',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'TWD',
+ namePlural: 'New Taiwan dollars',
+ },
+ {
+ name: 'Tanzanian Shilling',
+ symbol: 'TSh',
+ symbolNative: 'TSh',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'TZS',
+ namePlural: 'Tanzanian shillings',
+ },
+ {
+ name: 'Ukrainian Hryvnia',
+ symbol: '₴',
+ symbolNative: '₴',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'UAH',
+ namePlural: 'Ukrainian hryvnias',
+ },
+ {
+ name: 'Ugandan Shilling',
+ symbol: 'USh',
+ symbolNative: 'USh',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'UGX',
+ namePlural: 'Ugandan shillings',
+ },
+ {
+ name: 'Uruguayan Peso',
+ symbol: '$U',
+ symbolNative: '$',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'UYU',
+ namePlural: 'Uruguayan pesos',
+ },
+ {
+ name: 'Uzbekistan Som',
+ symbol: 'UZS',
+ symbolNative: 'UZS',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'UZS',
+ namePlural: 'Uzbekistan som',
+ },
+ {
+ name: 'Venezuelan Bolívar',
+ symbol: 'Bs.F.',
+ symbolNative: 'Bs.F.',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'VEF',
+ namePlural: 'Venezuelan bolívars',
+ },
+ {
+ name: 'Vietnamese Dong',
+ symbol: '₫',
+ symbolNative: '₫',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'VND',
+ namePlural: 'Vietnamese dong',
+ },
+ {
+ name: 'CFA Franc BEAC',
+ symbol: 'FCFA',
+ symbolNative: 'FCFA',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'XAF',
+ namePlural: 'CFA francs BEAC',
+ },
+ {
+ name: 'CFA Franc BCEAO',
+ symbol: 'CFA',
+ symbolNative: 'CFA',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'XOF',
+ namePlural: 'CFA francs BCEAO',
+ },
+ {
+ name: 'Yemeni Rial',
+ symbol: 'YR',
+ symbolNative: 'ر.ي.',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'YER',
+ namePlural: 'Yemeni rials',
+ },
+ {
+ name: 'South African Rand',
+ symbol: 'R',
+ symbolNative: 'R',
+ decimalDigits: 2,
+ rounding: 0,
+ code: 'ZAR',
+ namePlural: 'South African rand',
+ },
+ {
+ name: 'Zambian Kwacha',
+ symbol: 'ZK',
+ symbolNative: 'ZK',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'ZMK',
+ namePlural: 'Zambian kwachas',
+ },
+ {
+ name: 'Zimbabwean Dollar',
+ symbol: 'ZWL$',
+ symbolNative: 'ZWL$',
+ decimalDigits: 0,
+ rounding: 0,
+ code: 'ZWL',
+ namePlural: 'Zimbabwean Dollar',
+ },
+];
diff --git a/src/lib/utils.ts b/src/lib/utils.ts
new file mode 100644
index 0000000..2cb92be
--- /dev/null
+++ b/src/lib/utils.ts
@@ -0,0 +1,6 @@
+import { type ClassValue, clsx } from "clsx"
+import { twMerge } from "tailwind-merge"
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs))
+}
diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx
new file mode 100644
index 0000000..78bb413
--- /dev/null
+++ b/src/pages/_app.tsx
@@ -0,0 +1,119 @@
+import { type Session } from 'next-auth';
+import { SessionProvider, useSession } from 'next-auth/react';
+import { type AppType } from 'next/app';
+import { Poppins } from 'next/font/google';
+import { ThemeProvider } from '~/components/theme-provider';
+import { api } from '~/utils/api';
+import clsx from 'clsx';
+import Head from 'next/head';
+import { Toaster } from 'sonner';
+
+import '~/styles/globals.css';
+import { type NextPageWithUser } from '~/types';
+import { LoadingSpinner } from '~/components/ui/spinner';
+import { useEffect, useState } from 'react';
+import { useAddExpenseStore } from '~/store/addStore';
+import { useAppStore } from '~/store/appStore';
+
+const poppins = Poppins({ weight: ['200', '300', '400', '500', '600', '700'], subsets: ['latin'] });
+
+const MyApp: AppType<{ session: Session | null }> = ({
+ Component,
+ pageProps: { session, ...pageProps },
+}) => {
+ return (
+
+
+ SplitPro: Split Expenses with your friends for free
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {(Component as NextPageWithUser).auth ? (
+
+ ) : (
+
+ )}{' '}
+
+
+
+ );
+};
+
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+const Auth: React.FC<{ Page: NextPageWithUser; pageProps: any }> = ({ Page, pageProps }) => {
+ const { status, data } = useSession({ required: true });
+ const [showSpinner, setShowSpinner] = useState(false);
+
+ const { setCurrency } = useAddExpenseStore((s) => s.actions);
+ const { setWebPushPublicKey } = useAppStore((s) => s.actions);
+
+ const { data: webPushPublicKey } = api.user.getWebPushPublicKey.useQuery();
+
+ useEffect(() => {
+ setTimeout(() => {
+ setShowSpinner(true);
+ }, 300);
+ }, []);
+
+ useEffect(() => {
+ if (webPushPublicKey) {
+ setWebPushPublicKey(webPushPublicKey);
+ }
+ }, [webPushPublicKey, setWebPushPublicKey]);
+
+ useEffect(() => {
+ if (status === 'authenticated') {
+ setCurrency(data.user.currency);
+ }
+ }, [status, data?.user, setCurrency]);
+
+ if (status === 'loading') {
+ return (
+
+ {showSpinner ? : null}
+
+ );
+ }
+
+ return ;
+};
+
+export default api.withTRPC(MyApp);
diff --git a/src/pages/account.tsx b/src/pages/account.tsx
new file mode 100644
index 0000000..150f9b4
--- /dev/null
+++ b/src/pages/account.tsx
@@ -0,0 +1,225 @@
+import Head from 'next/head';
+import MainLayout from '~/components/Layout/MainLayout';
+import { Button } from '~/components/ui/button';
+import Link from 'next/link';
+import { UserAvatar } from '~/components/ui/avatar';
+import {
+ Bell,
+ ChevronRight,
+ Download,
+ DownloadCloud,
+ FileDown,
+ Github,
+ HeartHandshakeIcon,
+ Star,
+} from 'lucide-react';
+import { signOut } from 'next-auth/react';
+import { AppDrawer } from '~/components/ui/drawer';
+import { SubmitFeedback } from '~/components/Account/SubmitFeedback';
+import { UpdateDetails } from '~/components/Account/UpdateDetails';
+import { api } from '~/utils/api';
+import { type NextPageWithUser } from '~/types';
+import { toast } from 'sonner';
+import { env } from '~/env';
+import { SubscribeNotification } from '~/components/Account/SubscribeNotification';
+import { useState } from 'react';
+import { LoadingSpinner } from '~/components/ui/spinner';
+
+const AccountPage: NextPageWithUser = ({ user }) => {
+ const userQuery = api.user.me.useQuery();
+ const downloadQuery = api.user.downloadData.useMutation();
+
+ const [downloading, setDownloading] = useState(false);
+
+ async function downloadData() {
+ setDownloading(true);
+ const data = await downloadQuery.mutateAsync();
+ const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
+ const url = URL.createObjectURL(blob);
+ const link = document.createElement('a');
+ link.href = url;
+ link.download = 'splitpro_data.json';
+ link.click();
+ URL.revokeObjectURL(url);
+ setDownloading(false);
+ }
+
+ return (
+ <>
+
+ Account
+
+
+ Account}>
+
+
+
+
+
+
{userQuery.data?.name}
+
{user.email}
+
+
+
+ {!userQuery.isLoading ? (
+
+ ) : null}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Follow us on X
+
+
+
+
+
+
+
+
+ Star us on Github
+
+
+
+
+
+
+
+
+ Sponsor us
+
+
+
+
+
+
+
+
+
+
+ Write a review
+
+
+
+
+
+
+
+ Download App
+
+
+
+ }
+ leftAction="Close"
+ title="Download App"
+ className="h-[70vh]"
+ shouldCloseOnAction
+ >
+
+
You can download SplitPro as a PWA to your home screen
+
+
+ If you are using iOS, checkout this{' '}
+
+ video
+
+
+
+
+ If you are using Android, checkout this{' '}
+
+ Video
+
+
+
+
+
+
+
+ Download splitpro data
+
+ {downloading ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+ Import from Splitwise
+
+
+
+
+
+
+
+ signOut()}
+ >
+ Logout
+
+
+
+
+ >
+ );
+};
+
+AccountPage.auth = true;
+
+export default AccountPage;
diff --git a/src/pages/activity.tsx b/src/pages/activity.tsx
new file mode 100644
index 0000000..2be74a2
--- /dev/null
+++ b/src/pages/activity.tsx
@@ -0,0 +1,129 @@
+import Head from 'next/head';
+import MainLayout from '~/components/Layout/MainLayout';
+import { SplitType } from '@prisma/client';
+import { api } from '~/utils/api';
+import { format } from 'date-fns';
+import { UserAvatar } from '~/components/ui/avatar';
+import { toUIString } from '~/utils/numbers';
+import Link from 'next/link';
+import { type NextPageWithUser } from '~/types';
+import { type User } from 'next-auth';
+import { BalanceSkeleton } from '~/components/ui/skeleton';
+import useEnableAfter from '~/hooks/useEnableAfter';
+import { LoadingSpinner } from '~/components/ui/spinner';
+
+function getPaymentString(
+ user: User,
+ amount: number,
+ paidBy: number,
+ expenseUserAmt: number,
+ isSettlement: boolean,
+ currency: string,
+ isDeleted?: boolean,
+) {
+ if (isDeleted) {
+ return null;
+ } else if (isSettlement) {
+ return (
+
+ {user.id === paidBy ? 'You paid ' : 'You received '} {currency} {toUIString(amount)}
+
+ );
+ } else {
+ return (
+
+ {user.id === paidBy
+ ? `You lent ${currency}
+ ${toUIString(Math.abs(expenseUserAmt))}`
+ : `You owe ${currency} ${toUIString(expenseUserAmt)}`}
+
+ );
+ }
+}
+
+const ActivityPage: NextPageWithUser = ({ user }) => {
+ const expensesQuery = api.user.getAllExpenses.useQuery();
+ const showProgress = useEnableAfter(350);
+
+ return (
+ <>
+
+ Activity
+
+
+
+
+
+ {expensesQuery.isLoading ? (
+ showProgress ? (
+
+
+
+ ) : null
+ ) : (
+ <>
+ {!expensesQuery.data?.length ? (
+
No activities yet
+ ) : null}
+ {expensesQuery.data?.map((e) => (
+
+
+
+
+
+ {e.expense.deletedByUser ? (
+
+
+ {e.expense.deletedBy === user.id
+ ? 'You'
+ : e.expense.deletedByUser.name ?? e.expense.deletedByUser.email}
+
+ {' deleted the expense '}
+ {e.expense.name}
+
+ ) : (
+
+
+ {e.expense.paidBy === user.id
+ ? 'You'
+ : e.expense.paidByUser.name ?? e.expense.paidByUser.email}
+
+ {' paid for '}
+ {e.expense.name}
+
+ )}
+
+
+ {getPaymentString(
+ user,
+ e.expense.amount,
+ e.expense.paidBy,
+ e.amount,
+ e.expense.splitType === SplitType.SETTLEMENT,
+ e.expense.currency,
+ !!e.expense.deletedBy,
+ )}
+
+
+ {format(e.expense.expenseDate, 'dd MMM')}
+
+
+
+ ))}
+ >
+ )}
+
+
+
+
+ >
+ );
+};
+
+ActivityPage.auth = true;
+
+export default ActivityPage;
diff --git a/src/pages/add.tsx b/src/pages/add.tsx
new file mode 100644
index 0000000..f2ec3b1
--- /dev/null
+++ b/src/pages/add.tsx
@@ -0,0 +1,158 @@
+import Head from 'next/head';
+import { useRouter } from 'next/router';
+import React, { useEffect } from 'react';
+import { AddOrEditExpensePage } from '~/components/AddExpense/AddExpensePage';
+import MainLayout from '~/components/Layout/MainLayout';
+import { env } from '~/env';
+import { isStorageConfigured } from '~/server/storage';
+import { calculateSplitShareBasedOnAmount, useAddExpenseStore } from '~/store/addStore';
+import { type NextPageWithUser } from '~/types';
+import { api } from '~/utils/api';
+import { toFixedNumber, toInteger } from '~/utils/numbers';
+
+// 🧾
+
+const AddPage: NextPageWithUser<{
+ isStorageConfigured: boolean;
+ enableSendingInvites: boolean;
+}> = ({ user, isStorageConfigured, enableSendingInvites }) => {
+ const {
+ setCurrentUser,
+ setGroup,
+ setParticipants,
+ setCurrency,
+ setAmount,
+ setDescription,
+ setPaidBy,
+ setAmountStr,
+ setSplitType,
+ setExpenseDate,
+ } = useAddExpenseStore((s) => s.actions);
+ const currentUser = useAddExpenseStore((s) => s.currentUser);
+
+ useEffect(() => {
+ setCurrentUser({
+ ...user,
+ emailVerified: null,
+ name: user.name ?? null,
+ email: user.email ?? null,
+ image: user.image ?? null,
+ });
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ const router = useRouter();
+ const { friendId, groupId, expenseId } = router.query;
+
+ const _groupId = parseInt(groupId as string);
+ const _friendId = parseInt(friendId as string);
+ const _expenseId = expenseId as string;
+ const groupQuery = api.group.getGroupDetails.useQuery(
+ { groupId: _groupId },
+ { enabled: !!_groupId },
+ );
+
+ const friendQuery = api.user.getFriend.useQuery(
+ { friendId: _friendId },
+ { enabled: !!_friendId },
+ );
+
+ const expenseQuery = api.user.getExpenseDetails.useQuery(
+ { expenseId: _expenseId },
+ { enabled: !!_expenseId, refetchOnWindowFocus: false },
+ );
+
+ useEffect(() => {
+ // Set group
+ if (groupId && !groupQuery.isLoading && groupQuery.data && currentUser) {
+ setGroup(groupQuery.data);
+
+ setParticipants([
+ currentUser,
+ ...groupQuery.data.groupUsers.map((gu) => gu.user).filter((u) => u.id !== currentUser.id),
+ ]);
+ useAddExpenseStore.setState({ showFriends: false });
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [groupId, groupQuery.isLoading, groupQuery.data, currentUser]);
+
+ useEffect(() => {
+ if (friendId && currentUser && friendQuery.data) {
+ setParticipants([currentUser, friendQuery.data]);
+ useAddExpenseStore.setState({ showFriends: false });
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [friendId, friendQuery.isLoading, friendQuery.data, currentUser]);
+
+ useEffect(() => {
+ if (_expenseId && expenseQuery.data) {
+ console.log(
+ 'expenseQuery.data 123',
+ expenseQuery.data.expenseParticipants,
+ expenseQuery.data.splitType,
+ calculateSplitShareBasedOnAmount(
+ toFixedNumber(expenseQuery.data.amount),
+ expenseQuery.data.expenseParticipants.map((ep) => ({
+ ...ep.user,
+ amount: toFixedNumber(ep.amount),
+ })),
+ expenseQuery.data.splitType,
+ expenseQuery.data.paidByUser,
+ ),
+ );
+ expenseQuery.data.group && setGroup(expenseQuery.data.group);
+ setParticipants(
+ calculateSplitShareBasedOnAmount(
+ toFixedNumber(expenseQuery.data.amount),
+ expenseQuery.data.expenseParticipants.map((ep) => ({
+ ...ep.user,
+ amount: toFixedNumber(ep.amount),
+ })),
+ expenseQuery.data.splitType,
+ expenseQuery.data.paidByUser,
+ ),
+ );
+ setCurrency(expenseQuery.data.currency);
+ setAmountStr(toFixedNumber(expenseQuery.data.amount).toString());
+ setDescription(expenseQuery.data.name);
+ setPaidBy(expenseQuery.data.paidByUser);
+ setAmount(toFixedNumber(expenseQuery.data.amount));
+ setSplitType(expenseQuery.data.splitType);
+ useAddExpenseStore.setState({ showFriends: false });
+ setExpenseDate(expenseQuery.data.expenseDate);
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [_expenseId, expenseQuery.data]);
+
+ return (
+ <>
+
+ Add Expense
+
+
+ {currentUser && (!_expenseId || expenseQuery.data) ? (
+
+ ) : (
+
+ )}
+
+ >
+ );
+};
+
+AddPage.auth = true;
+
+export default AddPage;
+
+export async function getServerSideProps() {
+ return {
+ props: {
+ isStorageConfigured: !!isStorageConfigured(),
+ enableSendingInvites: !!env.ENABLE_SENDING_INVITES,
+ },
+ };
+}
diff --git a/src/pages/api/auth/[...nextauth].ts b/src/pages/api/auth/[...nextauth].ts
new file mode 100644
index 0000000..99e60cb
--- /dev/null
+++ b/src/pages/api/auth/[...nextauth].ts
@@ -0,0 +1,5 @@
+import NextAuth from "next-auth";
+
+import { authOptions } from "~/server/auth";
+
+export default NextAuth(authOptions);
diff --git a/src/pages/api/trpc/[trpc].ts b/src/pages/api/trpc/[trpc].ts
new file mode 100644
index 0000000..726fc82
--- /dev/null
+++ b/src/pages/api/trpc/[trpc].ts
@@ -0,0 +1,19 @@
+import { createNextApiHandler } from "@trpc/server/adapters/next";
+
+import { env } from "~/env";
+import { appRouter } from "~/server/api/root";
+import { createTRPCContext } from "~/server/api/trpc";
+
+// export API handler
+export default createNextApiHandler({
+ router: appRouter,
+ createContext: createTRPCContext,
+ onError:
+ env.NODE_ENV === "development"
+ ? ({ path, error }) => {
+ console.error(
+ `❌ tRPC failed on ${path ?? ""}: ${error.message}`
+ );
+ }
+ : undefined,
+});
diff --git a/src/pages/auth/signin.tsx b/src/pages/auth/signin.tsx
new file mode 100644
index 0000000..2a29cb8
--- /dev/null
+++ b/src/pages/auth/signin.tsx
@@ -0,0 +1,243 @@
+'use client';
+import { type ClientSafeProvider, getProviders, signIn } from 'next-auth/react';
+import Head from 'next/head';
+import { Button } from '~/components/ui/button';
+import Image from 'next/image';
+import { type GetServerSideProps } from 'next';
+import { getServerAuthSession } from '~/server/auth';
+import { useState } from 'react';
+import { Input } from '~/components/ui/input';
+import { env } from '~/env';
+import { z } from 'zod';
+import { useForm } from 'react-hook-form';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { type NextPage } from 'next';
+import {
+ Form,
+ FormControl,
+ FormDescription,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from '~/components/ui/form';
+import {
+ InputOTP,
+ InputOTPGroup,
+ InputOTPSeparator,
+ InputOTPSlot,
+} from '~/components/ui/input-otp';
+import { REGEXP_ONLY_DIGITS_AND_CHARS } from 'input-otp';
+import type { NextPageWithUser } from '~/types';
+import AddPage from '~/pages/add';
+
+const emailSchema = z.object({
+ email: z.string({ required_error: 'Email is required' }).email({ message: 'Invalid email' }),
+});
+
+const otpSchema = z.object({
+ otp: z.string({ required_error: 'OTP is required' }).length(5, { message: 'Invalid OTP' }),
+});
+
+const providerSvgs = {
+ github: (
+
+
+
+ ),
+ google: (
+
+
+
+ ),
+ authentik: (
+
+
+
+ ),
+};
+
+const Home: NextPage<{ feedbackEmail: string; providers: ClientSafeProvider[] }> = ({
+ providers,
+ feedbackEmail,
+}) => {
+ const [emailStatus, setEmailStatus] = useState<'idle' | 'sending' | 'success'>('idle');
+
+ const emailForm = useForm>({
+ resolver: zodResolver(emailSchema),
+ });
+
+ const otpForm = useForm>({
+ resolver: zodResolver(otpSchema),
+ });
+
+ async function onEmailSubmit(values: z.infer) {
+ setEmailStatus('sending');
+ await signIn('email', { email: values.email.toLowerCase(), redirect: false });
+ setEmailStatus('success');
+ }
+
+ async function onOTPSubmit(values: z.infer) {
+ const email = emailForm.getValues().email;
+ console.log('email', email);
+
+ const callbackUrl = window.location.origin;
+
+ window.location.href = `/api/auth/callback/email?email=${encodeURIComponent(
+ email.toLowerCase(),
+ )}&token=${values.otp.toLowerCase()}${callbackUrl ? `&callbackUrl=${callbackUrl}/balances` : ''}`;
+ }
+
+ return (
+ <>
+
+ SplitPro: Split Expenses with your friends for free
+
+
+
+
+
+
+ {providers
+ .filter((provider) => provider.id !== 'email')
+ .map((provider) => (
+
signIn(provider.id)}
+ key={provider.id}
+ >
+ {providerSvgs[provider.id as keyof typeof providerSvgs]}
+ Continue with {provider.name}
+
+ ))}
+ {providers && providers.length === 2 && (
+
+ )}
+ {providers.find((provider) => provider.id === 'email') ? (
+ emailStatus === 'success' ? (
+ <>
+
+ We have sent an email with the OTP. Please check your inbox
+
+
+
+ (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )}
+ />
+
+
+ Submit
+
+
+
+ >
+ ) : (
+ <>
+
+
+ (
+
+
+
+
+
+
+
+ )}
+ />
+
+ {emailStatus === 'sending' ? 'Sending...' : 'Send magic link'}
+
+
+
+ >
+ )
+ ) : null}
+
+ Trouble logging in? contact
+
+
+ {feedbackEmail ?? ''}
+
+
+
+
+ >
+ );
+};
+
+export default Home;
+
+export const getServerSideProps: GetServerSideProps = async (context) => {
+ const session = await getServerAuthSession(context);
+ const providers = await getProviders();
+
+ if (session) {
+ return {
+ redirect: {
+ destination: '/balances',
+ permanent: false,
+ },
+ };
+ }
+
+ return {
+ props: {
+ feedbackEmail: env.FEEDBACK_EMAIL ?? '',
+ providers: Object.values(providers ?? {}),
+ },
+ };
+};
diff --git a/src/pages/balances.tsx b/src/pages/balances.tsx
new file mode 100644
index 0000000..6db33b0
--- /dev/null
+++ b/src/pages/balances.tsx
@@ -0,0 +1,188 @@
+import Head from 'next/head';
+import MainLayout from '~/components/Layout/MainLayout';
+import clsx from 'clsx';
+import { Button } from '~/components/ui/button';
+import { ArrowUpOnSquareIcon } from '@heroicons/react/24/outline';
+import { type User } from '@prisma/client';
+import { api } from '~/utils/api';
+import Link from 'next/link';
+import { toUIString } from '~/utils/numbers';
+import { PlusIcon } from 'lucide-react';
+import { UserAvatar } from '~/components/ui/avatar';
+import InstallApp from '~/components/InstallApp';
+import { type NextPageWithUser } from '~/types';
+import useEnableAfter from '~/hooks/useEnableAfter';
+import { LoadingSpinner } from '~/components/ui/spinner';
+import { NotificationModal } from '~/components/NotificationModal';
+import { GetServerSideProps } from 'next';
+
+const BalancePage: NextPageWithUser = () => {
+ function shareWithFriends() {
+ if (navigator.share) {
+ navigator
+ .share({
+ title: 'SplitPro',
+ text: "Check out SplitPro. It's an open source free alternative for Splitwise",
+ url: 'https://splitpro.app',
+ })
+ .then(() => console.log('Successful share'))
+ .catch((error) => console.log('Error sharing', error));
+ }
+ }
+
+ const balanceQuery = api.user.getBalances.useQuery();
+ const showProgress = useEnableAfter(350);
+
+ return (
+ <>
+
+ Outstanding balances
+
+
+
+
+
+ ) : (
+
+ )
+ }
+ >
+
+
+
+ {balanceQuery.data?.youOwe.length ? (
+
+ {/*
*/}
+
+
+ {balanceQuery.data?.youOwe.map((b, index) => (
+
+
+ {b.currency.toUpperCase()} {toUIString(b.amount)}
+
+ {index !== balanceQuery.data.youOwe.length - 1 ? (
+ +
+ ) : null}
+
+ ))}
+
+
+ ) : null}
+ {balanceQuery.data?.youGet.length ? (
+
+
+
+ {balanceQuery.data?.youGet.map((b, index) => (
+
+
+ {b.currency.toUpperCase()} {toUIString(b.amount)}
+
{' '}
+ {index !== balanceQuery.data.youGet.length - 1 ? (
+ +
+ ) : null}
+
+ ))}
+
+
+ ) : null}
+
+
+
+ {balanceQuery.isLoading ? (
+ showProgress ? (
+
+
+
+ ) : null
+ ) : null}
+
+ {balanceQuery.data?.balances.map((b) => (
+
0}
+ currency={b.currency}
+ hasMore={b.hasMore}
+ />
+ ))}
+
+ {!balanceQuery.isLoading && !balanceQuery.data?.balances.length ? (
+
+
+
+
+
+
+ Add Expense
+
+
+
+ ) : null}
+
+
+
+ >
+ );
+};
+
+const FriendBalance: React.FC<{
+ friend: User;
+ amount: number;
+ isPositive: boolean;
+ currency: string;
+ id: number;
+ hasMore?: boolean;
+}> = ({ friend, amount, isPositive, currency, id, hasMore }) => {
+ return (
+
+
+
+
{friend.name ?? friend.email}
+
+ {amount === 0 ? (
+
+ ) : (
+
+
+ {isPositive ? 'you get' : 'you owe'}
+
+
+ {currency} {toUIString(amount)}
+ {hasMore ? '*' : ''}
+
+
+ )}
+
+ );
+};
+
+BalancePage.auth = true;
+
+// export const getServerSideProps = (async () => {
+
+// return { props: { webPushKey: env } };
+// }) satisfies GetServerSideProps<{ webPushKey: string }>;
+
+export default BalancePage;
diff --git a/src/pages/balances/[friendId].tsx b/src/pages/balances/[friendId].tsx
new file mode 100644
index 0000000..b53e523
--- /dev/null
+++ b/src/pages/balances/[friendId].tsx
@@ -0,0 +1,230 @@
+import Head from 'next/head';
+import MainLayout from '~/components/Layout/MainLayout';
+import { SplitType } from '@prisma/client';
+import { api } from '~/utils/api';
+import { UserAvatar } from '~/components/ui/avatar';
+import Link from 'next/link';
+import { ChevronLeftIcon, PlusIcon, Trash, Trash2 } from 'lucide-react';
+import { format } from 'date-fns';
+import { Separator } from '~/components/ui/separator';
+import { Button } from '~/components/ui/button';
+import { SettleUp } from '~/components/Friend/Settleup';
+import { toUIString } from '~/utils/numbers';
+import { CategoryIcon } from '~/components/ui/categoryIcons';
+import { useRouter } from 'next/router';
+import { type NextPageWithUser } from '~/types';
+import { motion } from 'framer-motion';
+import { DeleteFriend } from '~/components/Friend/DeleteFriend';
+import { Export } from '~/components/Friend/Export';
+
+const FriendPage: NextPageWithUser = ({ user }) => {
+ const router = useRouter();
+ const { friendId } = router.query;
+
+ const _friendId = parseInt(friendId as string);
+
+ const friendQuery = api.user.getFriend.useQuery(
+ { friendId: _friendId },
+ { enabled: !!_friendId },
+ );
+
+ const expenses = api.user.getExpensesWithFriend.useQuery(
+ { friendId: _friendId },
+ { enabled: !!_friendId },
+ );
+ const balances = api.user.getBalancesWithFriend.useQuery(
+ { friendId: _friendId },
+ { enabled: !!_friendId },
+ );
+
+ const youLent = balances.data?.filter((b) => b.amount > 0);
+ const youOwe = balances.data?.filter((b) => b.amount < 0);
+
+ return (
+ <>
+
+ Outstanding balances
+
+
+
+
+
+
+ {friendQuery.data?.name}
+
+ }
+ actions={
+
+ }
+ header={
+
+
+
+
+
+
+ {friendQuery.data?.name}
+
+
+ }
+ >
+ {balances.isLoading ||
+ expenses.isLoading ||
+ friendQuery.isLoading ||
+ !friendQuery.data ? null : (
+
+
+
+ {(youOwe?.length ?? 0) > 0 && (
+ <>
+ You owe{' '}
+ {youOwe?.map((b, index) => (
+
+
+ {b.currency} {toUIString(b.amount)}
+
+ {youOwe.length - 1 === index ? '' : ' + '}
+
+ ))}
+ >
+ )}
+
+
{(youOwe?.length ?? 0) > 0 && (youLent?.length ?? 0) > 0 ? '+' : null}
+
+ {(youLent?.length ?? 0) > 0 && (
+ <>
+ You lent{' '}
+ {youLent?.map((b, index) => (
+
+
+ {b.currency} {toUIString(b.amount)}
+
+ {youLent.length - 1 === index ? '' : ' + '}
+
+ ))}
+ >
+ )}
+
+
+
+ {balances.data ? (
+
+ ) : (
+
+ Settle up
+
+ )}
+
+
+
+ Add Expense
+
+
+
+ {/*
+
+ Remind
+ */}
+
+
+
+ {expenses.data?.map((e) => {
+ const youPaid = e.paidBy === user.id;
+ const yourExpense = e.expenseParticipants.find(
+ (p) => p.userId === (youPaid ? friendQuery.data?.id : user.id),
+ );
+ const isSettlement = e.splitType === SplitType.SETTLEMENT;
+
+ return (
+
+
+
+ {format(e.expenseDate, 'MMM dd')
+ .split(' ')
+ .map((d) => (
+
+ {d}
+
+ ))}
+
+
+
+
+
+ {!isSettlement ? (
+
+ {e.name}
+
+ ) : null}
+
+
+ {isSettlement ? ' 🎉 ' : null}
+
+
+ {youPaid ? 'You' : friendQuery.data?.name} paid {e.currency}{' '}
+ {toUIString(e.amount)}{' '}
+
+
+
+
+ {isSettlement ? null : (
+
+
+ {youPaid ? 'You lent' : 'You owe'}
+
+
+ {e.currency} {' '}
+ {toUIString(yourExpense?.amount ?? 0)}
+
+
+ )}
+
+ );
+ })}
+
+
+ )}
+
+ >
+ );
+};
+
+FriendPage.auth = true;
+
+export default FriendPage;
diff --git a/src/pages/balances/[friendId]/expenses/[expenseId].tsx b/src/pages/balances/[friendId]/expenses/[expenseId].tsx
new file mode 100644
index 0000000..2739938
--- /dev/null
+++ b/src/pages/balances/[friendId]/expenses/[expenseId].tsx
@@ -0,0 +1,75 @@
+import Head from 'next/head';
+import MainLayout from '~/components/Layout/MainLayout';
+import { api } from '~/utils/api';
+import Link from 'next/link';
+import { useRouter } from 'next/router';
+import { ChevronLeftIcon, PencilIcon, Trash, Trash2 } from 'lucide-react';
+import ExpenseDetails from '~/components/Expense/ExpensePage';
+import { DeleteExpense } from '~/components/Expense/DeleteExpense';
+import { type NextPageWithUser } from '~/types';
+import { env } from '~/env';
+import { Button } from '~/components/ui/button';
+
+const ExpensesPage: NextPageWithUser<{ storagePublicUrl?: string }> = ({
+ user,
+ storagePublicUrl,
+}) => {
+ const router = useRouter();
+ const expenseId = router.query.expenseId as string;
+ const friendId = parseInt(router.query.friendId as string);
+
+ const expenseQuery = api.user.getExpenseDetails.useQuery({ expenseId });
+
+ return (
+ <>
+
+ Outstanding balances
+
+
+
+
+
+
+ Expense details
+
+ }
+ actions={
+
+ }
+ >
+ {expenseQuery.data ? (
+
+ ) : null}
+
+ >
+ );
+};
+
+ExpensesPage.auth = true;
+
+export async function getServerSideProps() {
+ return {
+ props: {
+ storagePublicUrl: env.R2_PUBLIC_URL,
+ },
+ };
+}
+
+export default ExpensesPage;
diff --git a/src/pages/balances/expenses.tsx b/src/pages/balances/expenses.tsx
new file mode 100644
index 0000000..81773ee
--- /dev/null
+++ b/src/pages/balances/expenses.tsx
@@ -0,0 +1,17 @@
+import { type NextPage } from 'next';
+import { useRouter } from 'next/router';
+import React from 'react';
+import MainLayout from '~/components/Layout/MainLayout';
+
+const ExpensesPage: NextPage = () => {
+ return (
+
+
+
+ );
+};
+
+export default ExpensesPage;
diff --git a/src/pages/blog/need-for-splitwise-alternative.md b/src/pages/blog/need-for-splitwise-alternative.md
new file mode 100644
index 0000000..4d99871
--- /dev/null
+++ b/src/pages/blog/need-for-splitwise-alternative.md
@@ -0,0 +1,39 @@
+---
+title: A need for open source splitwise alternative
+date: 2024/3/17
+author: KM Koushik
+---
+
+Splitwise doesn't need an introduction. It's a popular app for managing shared expenses. It's advertised as a free tool but they slowly started charging money for very basic features like adding more than 3 expenses in a day.
+
+Don't get me wrong, I'm fine with products charging money. After all, splitwise is a very good product and lot's of effort has been put on the product. But the problem is locking you after you've used the product for a long time and charge for features that doesn't convert to the value they provide.
+
+I will happily pay for the pro features, but this really frustrates me.
+
+## Okay, what now?
+
+Creating an open source alternative is very important now. Any closed source alternative might do the same and I don't have any reason to believe otherwise. This will force us to create a better product and makes it hard to be evil. After all we will have an option to host the solution ourselves.
+
+Splitpro is designed to be an drop in open source replacement for Splitwise with all the important features. And completely free to use.
+
+- ➕ Add expenses with an individual or groups.
+- 👥 Overall balances across the groups.
+- 💵 Multiple currency support
+- 📄 Upload expense bills
+- 📱 PWA support
+- ✂️ Split expenses unequally (share, percentage, exact amounts)
+- 🔔 Push notification
+- ✅ Settle up expenses
+- 📲 Import data from Splitwise
+
+## Will you make it paid?
+
+Any existing features will not be paid. But I have plans to introduce paid features which are not core for this product. This is mainly to fund this project.
+
+## How are you running it for free?
+
+I working on a full time job and this is my side project. Even with very high usage, splitrpo won't cost more than $100 a month. I currently can afford this. As I said earlier I will add some paid pro features / open up donations to fund the project.
+
+## How can I help?
+
+Very simple! Use the product, share it with your friends and give feedback. If you want to contribute, head to our [github](https://github.com/oss-apps/split-pro) repo and open a PR.
diff --git a/src/pages/expenses/[expenseId].tsx b/src/pages/expenses/[expenseId].tsx
new file mode 100644
index 0000000..0d2a8e5
--- /dev/null
+++ b/src/pages/expenses/[expenseId].tsx
@@ -0,0 +1,76 @@
+import Head from 'next/head';
+import MainLayout from '~/components/Layout/MainLayout';
+import { getServerAuthSessionForSSG } from '~/server/auth';
+import { type User } from '@prisma/client';
+import { api } from '~/utils/api';
+import Link from 'next/link';
+import { useRouter } from 'next/router';
+import { ChevronLeftIcon, PencilIcon } from 'lucide-react';
+import ExpenseDetails from '~/components/Expense/ExpensePage';
+import { DeleteExpense } from '~/components/Expense/DeleteExpense';
+import { type NextPageWithUser } from '~/types';
+import { env } from 'process';
+import { Button } from '~/components/ui/button';
+
+const ExpensesPage: NextPageWithUser<{ storagePublicUrl?: string }> = ({
+ user,
+ storagePublicUrl,
+}) => {
+ const router = useRouter();
+ const expenseId = router.query.expenseId as string;
+
+ const expenseQuery = api.user.getExpenseDetails.useQuery({ expenseId });
+
+ return (
+ <>
+
+ Outstanding balances
+
+
+
+
+
+
+ Expense details
+
+ }
+ actions={
+
+ {!expenseQuery.data?.deletedBy ? (
+
+ ) : null}
+
+ }
+ >
+ {expenseQuery.data ? (
+
+ ) : null}
+
+ >
+ );
+};
+
+ExpensesPage.auth = true;
+
+export async function getServerSideProps() {
+ return {
+ props: {
+ storagePublicUrl: env.R2_PUBLIC_URL,
+ },
+ };
+}
+
+export default ExpensesPage;
diff --git a/src/pages/groups.tsx b/src/pages/groups.tsx
new file mode 100644
index 0000000..4c5643a
--- /dev/null
+++ b/src/pages/groups.tsx
@@ -0,0 +1,112 @@
+import Head from 'next/head';
+import MainLayout from '~/components/Layout/MainLayout';
+import clsx from 'clsx';
+import { Button } from '~/components/ui/button';
+import { PlusIcon } from '@heroicons/react/24/solid';
+import { getServerAuthSessionForSSG } from '~/server/auth';
+import { type User } from '@prisma/client';
+import { api } from '~/utils/api';
+import { CreateGroup } from '~/components/group/CreateGroup';
+import Link from 'next/link';
+import { GroupAvatar } from '~/components/ui/avatar';
+import { toUIString } from '~/utils/numbers';
+import { motion } from 'framer-motion';
+import { type NextPageWithUser } from '~/types';
+
+const BalancePage: NextPageWithUser = () => {
+ const groupQuery = api.group.getAllGroupsWithBalances.useQuery();
+
+ return (
+ <>
+
+ Groups
+
+
+
+
+
+ }
+ >
+
+
+ {groupQuery.isLoading ? null : groupQuery.data?.length === 0 ? (
+
+
+
+
+ Create Group
+
+
+
+ ) : (
+ groupQuery.data?.map((g) => {
+ const [amount, currency] = Object.keys(g.balances).length
+ ? [Object.values(g.balances)[0] ?? 0, Object.keys(g.balances)[0] ?? 'USD']
+ : [0, 'USD'];
+ return (
+
= 0 ? true : false}
+ currency={currency}
+ />
+ );
+ })
+ )}
+
+
+
+ >
+ );
+};
+
+const GroupBalance: React.FC<{
+ groupId: number;
+ name: string;
+ amount: number;
+ isPositive: boolean;
+ currency: string;
+}> = ({ name, amount, isPositive, currency, groupId }) => {
+ return (
+
+
+
+
+ {amount === 0 ? (
+
Settled up
+ ) : (
+ <>
+
+ {isPositive ? 'you lent' : 'you owe'}
+
+
+ {currency} {toUIString(amount)}
+
+ >
+ )}
+
+
+
+ );
+};
+
+BalancePage.auth = true;
+
+export default BalancePage;
\ No newline at end of file
diff --git a/src/pages/groups/[groupId].tsx b/src/pages/groups/[groupId].tsx
new file mode 100644
index 0000000..7e4d485
--- /dev/null
+++ b/src/pages/groups/[groupId].tsx
@@ -0,0 +1,455 @@
+import Head from 'next/head';
+import MainLayout from '~/components/Layout/MainLayout';
+import Avatar from 'boring-avatars';
+import clsx from 'clsx';
+import { Button } from '~/components/ui/button';
+import { SplitType } from '@prisma/client';
+import { api } from '~/utils/api';
+import { useRouter } from 'next/router';
+import {
+ Check,
+ ChevronLeft,
+ DoorOpen,
+ Share,
+ Trash2,
+ UserPlus,
+ BarChartHorizontal,
+ Info,
+} from 'lucide-react';
+import { AppDrawer } from '~/components/ui/drawer';
+import { UserAvatar } from '~/components/ui/avatar';
+import NoMembers from '~/components/group/NoMembers';
+import { format } from 'date-fns';
+import AddMembers from '~/components/group/AddMembers';
+import Image from 'next/image';
+import { toUIString } from '~/utils/numbers';
+import Link from 'next/link';
+import { CategoryIcon } from '~/components/ui/categoryIcons';
+import { env } from '~/env';
+import { useState } from 'react';
+import { type NextPageWithUser } from '~/types';
+import { motion } from 'framer-motion';
+import {
+ AlertDialog,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+ AlertDialogTrigger,
+} from '~/components/ui/alert-dialog';
+import { toast } from 'sonner';
+import GroupMyBalance from '~/components/group/GroupMyBalance';
+
+const BalancePage: NextPageWithUser<{
+ enableSendingInvites: boolean;
+}> = ({ user, enableSendingInvites }) => {
+ const router = useRouter();
+ const groupId = parseInt(router.query.groupId as string);
+
+ const groupDetailQuery = api.group.getGroupDetails.useQuery({ groupId });
+ const groupTotalQuery = api.group.getGroupTotals.useQuery({ groupId });
+ const expensesQuery = api.group.getExpenses.useQuery({ groupId });
+ const deleteGroupMutation = api.group.delete.useMutation();
+ const leaveGroupMutation = api.group.leaveGroup.useMutation();
+
+ const [isInviteCopied, setIsInviteCopied] = useState(false);
+ const [showDeleteTrigger, setShowDeleteTrigger] = useState(false);
+ const [showLeaveTrigger, setShowLeaveTrigger] = useState(false);
+
+ async function inviteMembers() {
+ if (!groupDetailQuery.data) return;
+ const inviteLink =
+ window.location.origin + '/join-group?groupId=' + groupDetailQuery.data.publicId;
+
+ if (navigator.share) {
+ navigator
+ .share({
+ title: `Join to ${groupDetailQuery.data.name} in Splitpro`,
+ text: 'Join to the group and you can add, manage expenses, and track your balances',
+ url: inviteLink,
+ })
+ .then(() => console.log('Successful share'))
+ .catch((error) => console.log('Error sharing', error));
+ } else {
+ await navigator.clipboard.writeText(inviteLink);
+ setIsInviteCopied(true);
+ setTimeout(() => {
+ setIsInviteCopied(false);
+ }, 2000);
+ }
+ }
+
+ const isAdmin = groupDetailQuery.data?.userId === user.id;
+ const canDelete =
+ groupDetailQuery.data?.userId === user.id &&
+ !groupDetailQuery.data.groupBalances.find((b) => b.amount !== 0);
+ const canLeave = !groupDetailQuery.data?.groupBalances.find(
+ (b) => b.amount !== 0 && b.userId === user.id,
+ );
+
+ function onGroupDelete() {
+ deleteGroupMutation.mutate(
+ { groupId },
+ {
+ onSuccess: () => {
+ router.replace('/groups').catch(console.error);
+ setShowDeleteTrigger(false);
+ },
+ onError: () => {
+ setShowDeleteTrigger(false);
+ toast.error('Something went wrong');
+ },
+ },
+ );
+ }
+
+ function onGroupLeave() {
+ leaveGroupMutation.mutate(
+ { groupId },
+ {
+ onSuccess: () => {
+ router.replace('/groups').catch(console.error);
+ setShowLeaveTrigger(false);
+ },
+ onError: () => {
+ setShowLeaveTrigger(false);
+ toast.error('Something went wrong');
+ },
+ },
+ );
+ }
+
+ console.log('expensesQuery.data', expensesQuery.data);
+
+ return (
+ <>
+
+ Group outstanding balances
+
+
+
+ {
+ await router.replace(`/groups`);
+ }}
+ className="mr-2 p-0"
+ >
+
+
+ {groupDetailQuery.data?.name}
+
+ }
+ actions={
+
+
}
+ className="h-[85vh]"
+ >
+
+
Total expenses
+
+ {groupTotalQuery.data?.map((total, index, arr) => {
+ return total._sum.amount != null ? (
+ <>
+
+ {total.currency} {toUIString(total._sum.amount)}
+
+ {index < arr.length - 1 ?
+ : null}
+ >
+ ) : null;
+ })}
+
+ {expensesQuery?.data && expensesQuery?.data?.length > 0 && (
+
+
First expense
+
+ {expensesQuery.data[expensesQuery.data.length - 1]?.createdAt
+ ? format(
+ expensesQuery.data[expensesQuery.data.length - 1]!.createdAt,
+ 'MMM dd, yyyy',
+ )
+ : '--'}
+
+
+ )}
+
+
+
}
+ className="h-[85vh]"
+ >
+
+
Members
+
+ {groupDetailQuery.data?.groupUsers.map((groupUser) => (
+
+
+
{groupUser.user.name ?? groupUser.user.email}
+
+ ))}
+
+
+ {groupDetailQuery?.data?.createdAt && (
+
+
Group created
+
{format(groupDetailQuery.data?.createdAt, 'MMM dd, yyyy')}
+
+ )}
+
+
Actions
+
+ {isAdmin ? (
+ <>
+
+ status !== showDeleteTrigger ? setShowDeleteTrigger(status) : null
+ }
+ >
+
+ setShowDeleteTrigger(true)}
+ >
+ Delete group
+
+
+
+
+
+ {canDelete ? 'Are you absolutely sure?' : ''}
+
+
+ {canDelete
+ ? 'This action cannot be reversed'
+ : "Can't delete the group until everyone settles up the balance"}
+
+
+
+ Cancel
+ {canDelete ? (
+
+ Delete
+
+ ) : null}
+
+
+
+ >
+ ) : (
+ <>
+
+ status !== showLeaveTrigger ? setShowLeaveTrigger(status) : null
+ }
+ >
+
+ setShowLeaveTrigger(true)}
+ >
+ Leave group
+
+
+
+
+
+ {canLeave ? 'Are you absolutely sure?' : ''}
+
+
+ {canLeave
+ ? 'This action cannot be reversed'
+ : "Can't leave the group until your outstanding balance is settled"}
+
+
+
+ Cancel
+ {canLeave ? (
+
+ Leave
+
+ ) : null}
+
+
+
+ >
+ )}
+
+
+
+
+ }
+ header={
+
+
{
+ await router.replace(`/groups`);
+ }}
+ className="mr-2 p-0"
+ >
+
+
+
+
{groupDetailQuery.data?.name}
+
+ }
+ >
+ {groupDetailQuery.isLoading ? null : groupDetailQuery.data?.groupUsers.length === 1 &&
+ !expensesQuery.data?.length ? (
+
+ ) : (
+
+
+ gu.user) ?? []}
+ />
+
+
+
+
+ Add Expense
+
+
+
+ {groupDetailQuery.data ? (
+
+ Add members
+
+ ) : null}
+
+
+ {isInviteCopied ? (
+ <>
+ Copied
+ >
+ ) : (
+ <>
+ Invite
+ >
+ )}
+
+
+
+ )}
+ {expensesQuery.data?.map((e) => {
+ const youPaid = e.paidBy === user.id;
+ const yourExpense = e.expenseParticipants.find((p) => p.userId === user.id);
+ const isSettlement = e.splitType === SplitType.SETTLEMENT;
+ const yourExpenseAmount = youPaid
+ ? yourExpense?.amount ?? 0
+ : -(yourExpense?.amount ?? 0);
+
+ return (
+
+
+
+ {format(e.expenseDate, 'MMM dd')
+ .split(' ')
+ .map((d) => (
+
+ {d}
+
+ ))}
+
+
+
+
+
+ {!isSettlement ? (
+
+ {e.name}
+
+ ) : null}
+
+ {isSettlement ? ' 🎉 ' : null}
+ {youPaid ? 'You' : e.paidByUser.name ?? e.paidByUser.email} paid {e.currency}{' '}
+ {toUIString(e.amount)}{' '}
+
+
+
+ {isSettlement ? null : (
+
+
+ {youPaid ? 'You lent' : 'You owe'}
+
+
+ {e.currency} {' '}
+ {toUIString(yourExpenseAmount)}
+
+
+ )}
+
+ );
+ })}
+ {expensesQuery.data?.length === 0 &&
+ !groupDetailQuery.isLoading &&
+ groupDetailQuery.data?.groupUsers.length !== 1 ? (
+
+
+
+ ) : null}
+
+ >
+ );
+};
+
+BalancePage.auth = true;
+
+export default BalancePage;
+
+export async function getServerSideProps() {
+ return {
+ props: {
+ enableSendingInvites: env.ENABLE_SENDING_INVITES,
+ },
+ };
+}
diff --git a/src/pages/groups/[groupId]/expenses/[expenseId].tsx b/src/pages/groups/[groupId]/expenses/[expenseId].tsx
new file mode 100644
index 0000000..a5f4a91
--- /dev/null
+++ b/src/pages/groups/[groupId]/expenses/[expenseId].tsx
@@ -0,0 +1,77 @@
+import Head from 'next/head';
+import MainLayout from '~/components/Layout/MainLayout';
+import { getServerAuthSessionForSSG } from '~/server/auth';
+import { type User } from '@prisma/client';
+import { api } from '~/utils/api';
+import Link from 'next/link';
+import { useRouter } from 'next/router';
+import { ChevronLeftIcon, PencilIcon } from 'lucide-react';
+import ExpenseDetails from '~/components/Expense/ExpensePage';
+import { DeleteExpense } from '~/components/Expense/DeleteExpense';
+import { type NextPageWithUser } from '~/types';
+import { env } from 'process';
+import { Button } from '~/components/ui/button';
+
+const ExpensesPage: NextPageWithUser<{ storagePublicUrl?: string }> = ({
+ user,
+ storagePublicUrl,
+}) => {
+ const router = useRouter();
+ const expenseId = router.query.expenseId as string;
+ const groupId = parseInt(router.query.groupId as string);
+
+ const expenseQuery = api.group.getExpenseDetails.useQuery({ expenseId, groupId });
+
+ return (
+ <>
+
+ Outstanding balances
+
+
+
+
+
+
+ Expense details
+
+
+ }
+ actions={
+
+ }
+ >
+ {expenseQuery.data ? (
+
+ ) : null}
+
+ >
+ );
+};
+
+ExpensesPage.auth = true;
+
+export async function getServerSideProps() {
+ return {
+ props: {
+ storagePublicUrl: env.R2_PUBLIC_URL,
+ },
+ };
+}
+
+export default ExpensesPage;
diff --git a/src/pages/import-splitwise.tsx b/src/pages/import-splitwise.tsx
new file mode 100644
index 0000000..8f39e08
--- /dev/null
+++ b/src/pages/import-splitwise.tsx
@@ -0,0 +1,245 @@
+import { type GetServerSideProps, type NextPage } from 'next';
+import Head from 'next/head';
+import { type User } from '@prisma/client';
+import { Input } from '~/components/ui/input';
+import { toast } from 'sonner';
+import { useState } from 'react';
+import { type SplitwiseGroup, type NextPageWithUser, type SplitwiseUser } from '~/types';
+import { api } from '~/utils/api';
+import { Button } from '~/components/ui/button';
+import MainLayout from '~/components/Layout/MainLayout';
+import { Separator } from '~/components/ui/separator';
+import Link from 'next/link';
+import { Checkbox } from '~/components/ui/checkbox';
+import { DownloadCloud } from 'lucide-react';
+import { LoadingSpinner } from '~/components/ui/spinner';
+import { useRouter } from 'next/router';
+import { PaperClipIcon } from '@heroicons/react/24/solid';
+
+const ImportSpliwisePage: NextPageWithUser = () => {
+ const [usersWithBalance, setUsersWithBalance] = useState>([]);
+ const [groups, setGroups] = useState>([]);
+ const [selectedUsers, setSelectedUsers] = useState>({});
+ const [selectedGroups, setSelectedGroups] = useState>({});
+ const [uploadedFile, setUploadedFile] = useState(null);
+
+ const router = useRouter();
+
+ const handleFileChange = async (event: React.ChangeEvent) => {
+ const files = event.target.files;
+
+ const file = files?.[0];
+
+ if (!file) return;
+
+ setUploadedFile(file);
+
+ try {
+ const json = JSON.parse(await file.text()) as Record;
+ const friendsWithOutStandingBalance: Array = [];
+ for (const friend of json.friends as Array>) {
+ const balance = friend.balance as Array<{ currency_code: string; amount: string }>;
+ if (balance.length && friend.registration_status === 'confirmed') {
+ friendsWithOutStandingBalance.push(friend as SplitwiseUser);
+ }
+ }
+
+ setUsersWithBalance(friendsWithOutStandingBalance);
+ setSelectedUsers(
+ friendsWithOutStandingBalance.reduce(
+ (acc, user) => {
+ acc[user.id] = true;
+ return acc;
+ },
+ {} as Record,
+ ),
+ );
+
+ const _groups = (json.groups as Array).filter(
+ (g) => g.members.length > 0 && g.id !== 0,
+ );
+
+ setGroups(_groups);
+ setSelectedGroups(
+ _groups.reduce(
+ (acc, group) => {
+ acc[group.id] = true;
+ return acc;
+ },
+ {} as Record,
+ ),
+ );
+
+ console.log('Friends with outstanding balance', friendsWithOutStandingBalance);
+ } catch (e) {
+ console.error(e);
+ toast.error('Error importing file');
+ }
+ };
+
+ const importMutation = api.user.importUsersFromSplitWise.useMutation();
+
+ function onImport() {
+ importMutation.mutate(
+ {
+ usersWithBalance: usersWithBalance.filter((user) => selectedUsers[user.id]),
+ groups: groups.filter((group) => selectedGroups[group.id]),
+ },
+ {
+ onSuccess: () => {
+ toast.success('Import successful');
+ router.push('/balances').catch((err) => console.error(err));
+ },
+ },
+ );
+ }
+
+ return (
+ <>
+
+ Import from splitwise
+
+
+
+
+
+
+
+
+ Cancel
+
+
+
+
Import from splitwise
+
+
+ Import
+
+
+
+
+
+
+
+
+ {uploadedFile ? uploadedFile.name : 'No file chosen'}
+
+
+
+
+
+ {importMutation.isLoading ? : 'Import'}
+
+
+
+ Note: It currently only supports importing friends and groups. It will not import
+ transactions. We are working on it.
+
+
+ {uploadedFile ? (
+ <>
+
Friends ({usersWithBalance.length})
+ {usersWithBalance.length ? (
+
+ {usersWithBalance.map((user, index) => (
+
+
+
+
{
+ setSelectedUsers({ ...selectedUsers, [user.id]: checked });
+ }}
+ />
+
+
{`${user.first_name}${user.last_name ? ' ' + user.last_name : ''}`}
+
+
+
+ {user.balance.map((b, index) => (
+ 0 ? 'text-green-500' : 'text-orange-600'}`}
+ >
+ {b.currency_code} {Math.abs(Number(b.amount)).toFixed(2)}
+
+ {index !== user.balance.length - 1 ? ' + ' : ''}
+
+
+ ))}
+
+
+
+ {index !== usersWithBalance.length - 1 ? : null}
+
+
+ ))}
+
+ ) : null}
+
Groups ({groups.length})
+ {groups.length ? (
+
+ {groups.map((group, index) => (
+
+
+
+
{
+ setSelectedGroups({ ...selectedGroups, [group.id]: checked });
+ }}
+ />
+
+
+
+ {group.members.length} members
+
+
+ {index !== groups.length - 1 ?
: null}
+
+ ))}
+
+ ) : null}
+ >
+ ) : (
+
+ Follow this link to export splitwise data
+
+
+
+ Export splitwise data
+
+
+
+ )}
+
+
+ >
+ );
+};
+
+ImportSpliwisePage.auth = true;
+
+export default ImportSpliwisePage;
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
new file mode 100644
index 0000000..2279c16
--- /dev/null
+++ b/src/pages/index.tsx
@@ -0,0 +1,245 @@
+import Head from 'next/head';
+import Link from 'next/link';
+import { Button } from '~/components/ui/button';
+import {
+ ArrowRight,
+ Banknote,
+ Bell,
+ FileUp,
+ GitFork,
+ Github,
+ Globe,
+ Import,
+ Split,
+ Users,
+} from 'lucide-react';
+import Image from 'next/image';
+import { BackgroundGradient } from '~/components/ui/background-gradient';
+import { env } from '~/env';
+
+export default function Home() {
+ return (
+ <>
+
+ SplitPro: Split Expenses with your friends for free
+
+
+ {process.env.NODE_ENV === 'production' && (
+ <>
+
+
+ {/* eslint-disable-next-line @next/next/no-img-element */}
+
+
+ >
+ )}
+
+
+
+
+
+ Why?
+ Terms
+ Privacy
+
+
+
+
+
+
+ Split Expenses with your friends for{' '}
+ free .
+
+
+ An{' '}
+
+ open source
+ {' '}
+ alternative to SplitWise
+
+
+
+
+ Add Expense {' '}
+
+
+
+
+ Star us on github
+
+
+
+
+
+
+
+
+
+
Features
+
+
+
+
+
+ Can create multiple groups or add balance directly. Everything will be
+ consolidated
+
+
+
+
+
+
+
Multiple currencies
+
+
+ Need to add expense with different currency for same user? No problem!
+
+
+
+
+
+
+
+
+ Advanced split options. By shares, percentage or exact amounts.
+
+
+
+
+
+
+ Love mobile apps? We got you covered. Install it as a PWA and you won't
+ even notice!
+
+
+
+
+
+
+
+
+ Upload receipts along with the expense
+
+
+
+
+
+
+ Which makes it hard to become evil. Easy to self host
+
+
+
+
+
+
+
+
Import from splitwise
+
+
+ Don't have to manually migrate balances. You can import users and groups
+ from splitwise
+
+
+
+
+
+ Never miss important notifications. Get notified when someone adds an expense or
+ settles up
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
+
+const MobileScreenShot = () => {
+ return (
+
+
+
+ );
+};
diff --git a/src/pages/join-group.tsx b/src/pages/join-group.tsx
new file mode 100644
index 0000000..666c9bd
--- /dev/null
+++ b/src/pages/join-group.tsx
@@ -0,0 +1,36 @@
+import { type GetServerSideProps } from 'next';
+import { joinGroup } from '~/server/api/services/splitService';
+import { getServerAuthSession } from '~/server/auth';
+
+export default function Home() {
+ return <>hello>;
+}
+
+export const getServerSideProps: GetServerSideProps = async (context) => {
+ const session = await getServerAuthSession(context);
+ const {
+ query: { groupId },
+ } = context;
+
+ if (!session || !groupId) {
+ return {
+ redirect: {
+ destination: '/',
+ permanent: false,
+ },
+ };
+ }
+
+ try {
+ await joinGroup(session.user.id, groupId as string);
+ } catch (e) {
+ console.log(e);
+ }
+
+ return {
+ redirect: {
+ destination: '/groups',
+ permanent: false,
+ },
+ };
+};
diff --git a/src/pages/privacy.tsx b/src/pages/privacy.tsx
new file mode 100644
index 0000000..b61d265
--- /dev/null
+++ b/src/pages/privacy.tsx
@@ -0,0 +1,112 @@
+import { type NextPage } from 'next';
+import Link from 'next/link';
+import { env } from '~/env';
+
+const Privacy: NextPage<{ feedbackEmail: string }> = ({ feedbackEmail }) => {
+ return (
+ <>
+
+
+
+
+
+
+
+ Privacy Policy
+ Last Updated: 06 Mar, 2022
+
+
+ Splitpro is committed to protecting your privacy. This Privacy Policy outlines how we
+ collect, use, and disclose your information when you use Splitpro.
+
+
+ Information We Collect
+ Personal Information
+
+ When you create an account, we collect your email address, name, photo (If you choose to
+ use continue with google)
+
+ Usage Data
+
+ We automatically collect information about how you interact with the Service, such as
+ pages visited and features used.
+
+
+ How We Use Your Information
+
+ We use your information for the following purposes:
+
+ To provide and maintain the Service
+ To improve and personalize your experience with the Service
+ To communicate with you about updates, promotions, and customer support
+ To show people your name and photo if they have your email id
+
+
+ Sharing Your Information
+
+ We do not sell, rent, or share your personal information with third parties.
+
+ 4. Data Security
+
+ We take reasonable steps to protect your information from unauthorized access, use, or
+ disclosure. However, no method of transmission or storage is completely secure, and we
+ cannot guarantee the absolute security of your information.
+
+
+ 5. Data Retention
+
+ We retain your personal information for as long as necessary to provide the Service,
+ comply with legal obligations, resolve disputes, and enforce our agreements.
+
+
+ 6. Your Rights
+
+ You may access, update, or request the deletion of your personal information by
+ contacting us at{' '}
+
+ {feedbackEmail ?? ''}
+
+ .
+
+
+ 7. Children's Privacy
+
+ The Service is not intended for users under 13 years old. We do not knowingly collect
+ personal information from children under 13. If you are a parent or guardian and believe
+ your child has provided us with personal information, please contact us at{' '}
+
+ {feedbackEmail ?? ''}
+
+ .
+
+ 8. Changes to This Policy
+
+ We may update this Policy from time to time. We will notify you of any changes by
+ posting the updated Policy on this page. By continuing to use the Service, you agree to
+ be bound by the updated Policy.
+
+ 9. Contact
+
+ If you have any questions or concerns regarding these Terms, please contact us at{' '}
+
+ {feedbackEmail ?? ''}
+
+ .
+
+
+
+ >
+ );
+};
+
+export default Privacy;
+
+export async function getServerSideProps() {
+ return {
+ props: {
+ feedbackEmail: env.FEEDBACK_EMAIL ?? '',
+ },
+ };
+}
diff --git a/src/pages/terms.tsx b/src/pages/terms.tsx
new file mode 100644
index 0000000..336ac9f
--- /dev/null
+++ b/src/pages/terms.tsx
@@ -0,0 +1,86 @@
+import { type NextPage } from 'next';
+import Head from 'next/head';
+import Link from 'next/link';
+import { env } from '~/env';
+
+const Terms: NextPage<{ feedbackEmail: string }> = ({ feedbackEmail }) => {
+ return (
+ <>
+
+
+
+
+
+
+
+ Terms of service
+ Last Updated: 06 Mar, 2022
+
+ 1. Agreement
+
+ By using Splitpro, you agree to these Terms of Service. Splitpro reserves the right to
+ modify these Terms at any time. By continuing to use the Service, you agree to the
+ updated Terms.
+
+
+ 2. Eligibility
+
+ You must be at least 13 years old to use the Service. By using the Service, you
+ represent that you meet this age requirement.
+
+
+ 3. Acceptable Use
+
+ You agree not to use the Service for any illegal or harmful activities. We reserve the
+ right to terminate your access to the Service if you violate this provision.
+
+
+ 4. Termination
+
+ We reserve the right to suspend or terminate your access to the Service at any time,
+ with or without notice, for any reason.
+
+
+ 5. Disclaimers and Limitation of Liability
+
+ The Service is provided "as is" and "as available," without
+ warranties of any kind. We disclaim all liability for any damages or losses arising from
+ your use of the Service.
+
+
+ 6. Governing Law
+
+ These Terms shall be governed by the laws of US. Any disputes arising from these Terms
+ shall be resolved in the courts located in US.
+
+
+ 7. Privacy
+
+ Please read our Privacy Policy
+
+
+ 8. Contact
+
+ If you have any questions or concerns regarding these Terms, please contact us at{' '}
+
+ {feedbackEmail ?? ''}
+
+ .
+
+
+
+ >
+ );
+};
+
+export default Terms;
+
+export async function getServerSideProps() {
+ return {
+ props: {
+ feedbackEmail: env.FEEDBACK_EMAIL ?? '',
+ },
+ };
+}
diff --git a/src/server/api/root.ts b/src/server/api/root.ts
new file mode 100644
index 0000000..784ac7a
--- /dev/null
+++ b/src/server/api/root.ts
@@ -0,0 +1,16 @@
+import { groupRouter } from '~/server/api/routers/group';
+import { createTRPCRouter } from '~/server/api/trpc';
+import { userRouter } from './routers/user';
+
+/**
+ * This is the primary router for your server.
+ *
+ * All routers added in /api/routers should be manually added here.
+ */
+export const appRouter = createTRPCRouter({
+ group: groupRouter,
+ user: userRouter,
+});
+
+// export type definition of API
+export type AppRouter = typeof appRouter;
diff --git a/src/server/api/routers/group.ts b/src/server/api/routers/group.ts
new file mode 100644
index 0000000..8e0066a
--- /dev/null
+++ b/src/server/api/routers/group.ts
@@ -0,0 +1,353 @@
+import { SplitType } from '@prisma/client';
+import { z } from 'zod';
+import { createTRPCRouter, groupProcedure, protectedProcedure } from '~/server/api/trpc';
+import { db } from '~/server/db';
+import { createGroupExpense, deleteExpense, editExpense } from '../services/splitService';
+import { TRPCError } from '@trpc/server';
+import { nanoid } from 'nanoid';
+
+export const groupRouter = createTRPCRouter({
+ create: protectedProcedure
+ .input(z.object({ name: z.string().min(1), currency: z.string().optional() }))
+ .mutation(async ({ ctx, input }) => {
+ const group = await ctx.db.group.create({
+ data: {
+ name: input.name,
+ publicId: nanoid(),
+ userId: ctx.session.user.id,
+ defaultCurrency: input.currency ?? 'USD',
+ groupUsers: {
+ create: {
+ userId: ctx.session.user.id,
+ },
+ },
+ },
+ });
+
+ return group;
+ }),
+
+ getAllGroups: protectedProcedure.query(async ({ ctx }) => {
+ const groups = await ctx.db.groupUser.findMany({
+ where: {
+ userId: ctx.session.user.id,
+ },
+ include: {
+ group: {
+ include: {
+ groupUsers: {
+ include: {
+ user: true,
+ },
+ },
+ },
+ },
+ },
+ });
+
+ return groups;
+ }),
+
+ getAllGroupsWithBalances: protectedProcedure.query(async ({ ctx }) => {
+ const time = Date.now();
+ const groups = await ctx.db.groupUser.findMany({
+ where: {
+ userId: ctx.session.user.id,
+ },
+ include: {
+ group: {
+ include: {
+ groupBalances: {
+ where: { userId: ctx.session.user.id },
+ },
+ expenses: {
+ orderBy: {
+ createdAt: 'desc',
+ },
+ take: 1,
+ },
+ },
+ },
+ },
+ });
+
+ const sortedGroupsByLatestExpense = groups.sort((a, b) => {
+ const aDate = a.group.expenses[0]?.createdAt ?? new Date(0);
+ const bDate = b.group.expenses[0]?.createdAt ?? new Date(0);
+ return bDate.getTime() - aDate.getTime();
+ });
+
+ const groupsWithBalances = sortedGroupsByLatestExpense.map((g) => {
+ const balances: Record = {};
+
+ for (const balance of g.group.groupBalances) {
+ balances[balance.currency] = (balances[balance.currency] ?? 0) + balance.amount;
+ }
+
+ return {
+ ...g.group,
+ balances,
+ };
+ });
+
+ return groupsWithBalances;
+ }),
+
+ joinGroup: protectedProcedure
+ .input(z.object({ groupId: z.string() }))
+ .mutation(async ({ ctx, input }) => {
+ const group = await ctx.db.group.findFirst({
+ where: {
+ publicId: input.groupId,
+ },
+ });
+
+ if (!group) {
+ throw new Error('Group not found');
+ }
+
+ await ctx.db.groupUser.create({
+ data: {
+ groupId: group.id,
+ userId: ctx.session.user.id,
+ },
+ });
+
+ return group;
+ }),
+
+ addOrEditExpense: groupProcedure
+ .input(
+ z.object({
+ paidBy: z.number(),
+ name: z.string(),
+ category: z.string(),
+ amount: z.number(),
+ splitType: z.enum([
+ SplitType.ADJUSTMENT,
+ SplitType.EQUAL,
+ SplitType.PERCENTAGE,
+ SplitType.SHARE,
+ SplitType.EXACT,
+ SplitType.SETTLEMENT,
+ ]),
+ currency: z.string(),
+ participants: z.array(z.object({ userId: z.number(), amount: z.number() })),
+ fileKey: z.string().optional(),
+ expenseDate: z.date().optional(),
+ expenseId: z.string().optional(),
+ }),
+ )
+ .mutation(async ({ input, ctx }) => {
+ if (input.expenseId) {
+ const expenseParticipant = await db.expenseParticipant.findUnique({
+ where: {
+ expenseId_userId: {
+ expenseId: input.expenseId,
+ userId: ctx.session.user.id,
+ },
+ },
+ });
+
+ if (!expenseParticipant) {
+ throw new TRPCError({
+ code: 'UNAUTHORIZED',
+ message: 'You are not the participant of the expense',
+ });
+ }
+ }
+
+ try {
+ const expense = input.expenseId
+ ? await editExpense(
+ input.expenseId,
+ input.paidBy,
+ input.name,
+ input.category,
+ input.amount,
+ input.splitType,
+ input.currency,
+ input.participants,
+ ctx.session.user.id,
+ input.expenseDate ?? new Date(),
+ input.fileKey,
+ )
+ : await createGroupExpense(
+ input.groupId,
+ input.paidBy,
+ input.name,
+ input.category,
+ input.amount,
+ input.splitType,
+ input.currency,
+ input.participants,
+ ctx.session.user.id,
+ input.expenseDate ?? new Date(),
+ input.fileKey,
+ );
+
+ return expense;
+ } catch (error) {
+ console.error(error);
+ throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Failed to create expense' });
+ }
+ }),
+
+ getExpenses: groupProcedure
+ .input(z.object({ groupId: z.number() }))
+ .query(async ({ input, ctx }) => {
+ const expenses = await ctx.db.expense.findMany({
+ where: {
+ groupId: input.groupId,
+ deletedBy: null,
+ },
+ orderBy: {
+ expenseDate: 'desc',
+ },
+ include: {
+ expenseParticipants: true,
+ paidByUser: true,
+ deletedByUser: true,
+ },
+ });
+
+ return expenses;
+ }),
+
+ getExpenseDetails: groupProcedure
+ .input(z.object({ expenseId: z.string() }))
+ .query(async ({ input }) => {
+ const expense = await db.expense.findUnique({
+ where: {
+ id: input.expenseId,
+ },
+ include: {
+ expenseParticipants: {
+ include: {
+ user: true,
+ },
+ },
+ expenseNotes: true,
+ addedByUser: true,
+ paidByUser: true,
+ deletedByUser: true,
+ updatedByUser: true,
+ },
+ });
+
+ return expense;
+ }),
+
+ getGroupDetails: groupProcedure.query(async ({ input, ctx }) => {
+ const group = await ctx.db.group.findUnique({
+ where: {
+ id: input.groupId,
+ },
+ include: {
+ groupUsers: {
+ include: {
+ user: true,
+ },
+ },
+ groupBalances: true,
+ },
+ });
+
+ return group;
+ }),
+
+ getGroupTotals: groupProcedure.query(async ({ input, ctx }) => {
+ const totals = await ctx.db.expense.groupBy({
+ by: 'currency',
+ _sum: {
+ amount: true,
+ },
+ where: {
+ groupId: input.groupId,
+ deletedAt: null,
+ },
+ });
+
+ return totals;
+ }),
+
+ addMembers: groupProcedure
+ .input(z.object({ userIds: z.array(z.number()) }))
+ .mutation(async ({ input, ctx }) => {
+ console.log(input.userIds);
+ const groupUsers = await ctx.db.groupUser.createMany({
+ data: input.userIds.map((userId) => ({
+ groupId: input.groupId,
+ userId,
+ })),
+ });
+
+ return groupUsers;
+ }),
+
+ leaveGroup: groupProcedure
+ .input(z.object({ groupId: z.number() }))
+ .mutation(async ({ input, ctx }) => {
+ const nonZeroBalance = await ctx.db.groupBalance.findFirst({
+ where: {
+ groupId: input.groupId,
+ userId: ctx.session.user.id,
+ amount: {
+ not: 0,
+ },
+ },
+ });
+
+ if (nonZeroBalance) {
+ throw new TRPCError({
+ code: 'BAD_REQUEST',
+ message: 'You have a non-zero balance in this group',
+ });
+ }
+
+ const groupUser = await ctx.db.groupUser.delete({
+ where: {
+ groupId_userId: {
+ groupId: input.groupId,
+ userId: ctx.session.user.id,
+ },
+ },
+ });
+
+ return groupUser;
+ }),
+
+ delete: groupProcedure
+ .input(z.object({ groupId: z.number() }))
+ .mutation(async ({ input, ctx }) => {
+ const group = await ctx.db.group.findUnique({
+ where: {
+ id: input.groupId,
+ },
+ include: {
+ groupBalances: true,
+ },
+ });
+
+ if (group?.userId !== ctx.session.user.id) {
+ throw new TRPCError({ code: 'UNAUTHORIZED', message: 'Only creator can delete the group' });
+ }
+
+ const balanceWithNonZero = group?.groupBalances.find((b) => b.amount !== 0);
+
+ if (balanceWithNonZero) {
+ throw new TRPCError({
+ code: 'BAD_REQUEST',
+ message: 'You have a non-zero balance in this group',
+ });
+ }
+
+ await ctx.db.group.delete({
+ where: {
+ id: input.groupId,
+ },
+ });
+
+ return group;
+ }),
+});
diff --git a/src/server/api/routers/user.ts b/src/server/api/routers/user.ts
new file mode 100644
index 0000000..fc623eb
--- /dev/null
+++ b/src/server/api/routers/user.ts
@@ -0,0 +1,523 @@
+import { type Balance, SplitType } from '@prisma/client';
+import { boolean, z } from 'zod';
+import { createTRPCRouter, protectedProcedure } from '~/server/api/trpc';
+import { db } from '~/server/db';
+import {
+ addUserExpense,
+ deleteExpense,
+ editExpense,
+ getCompleteFriendsDetails,
+ getCompleteGroupDetails,
+ importGroupFromSplitwise,
+ importUserBalanceFromSplitWise,
+} from '../services/splitService';
+import { TRPCError } from '@trpc/server';
+import { randomUUID } from 'crypto';
+import { getDocumentUploadUrl } from '~/server/storage';
+import { FILE_SIZE_LIMIT } from '~/lib/constants';
+import { sendFeedbackEmail, sendInviteEmail } from '~/server/mailer';
+import { pushNotification } from '~/server/notification';
+import { toFixedNumber, toUIString } from '~/utils/numbers';
+import { SplitwiseGroupSchema, SplitwiseUserSchema } from '~/types';
+import { env } from '~/env';
+
+export const userRouter = createTRPCRouter({
+ me: protectedProcedure.query(async ({ ctx }) => {
+ return ctx.session.user;
+ }),
+
+ getBalances: protectedProcedure.query(async ({ ctx }) => {
+ const balancesRaw = await db.balance.findMany({
+ where: {
+ userId: ctx.session.user.id,
+ },
+ orderBy: {
+ amount: 'desc',
+ },
+ include: {
+ friend: true,
+ },
+ });
+
+ const balances = balancesRaw
+ .reduce(
+ (acc, current) => {
+ const existing = acc.findIndex((item) => item.friendId === current.friendId);
+ if (existing === -1) {
+ acc.push(current);
+ } else {
+ const existingItem = acc[existing];
+ if (existingItem) {
+ if (Math.abs(existingItem.amount) > Math.abs(current.amount)) {
+ acc[existing] = { ...existingItem, hasMore: true };
+ } else {
+ acc[existing] = { ...current, hasMore: true };
+ }
+ }
+ }
+ return acc;
+ },
+ [] as ((typeof balancesRaw)[number] & { hasMore?: boolean })[],
+ )
+ .sort((a, b) => Math.abs(b.amount) - Math.abs(a.amount));
+
+ const cumulatedBalances = await db.balance.groupBy({
+ by: ['currency'],
+ _sum: {
+ amount: true,
+ },
+ where: {
+ userId: ctx.session.user.id,
+ },
+ });
+
+ const youOwe: Array<{ currency: string; amount: number }> = [];
+ const youGet: Array<{ currency: string; amount: number }> = [];
+
+ for (const b of cumulatedBalances) {
+ const sumAmount = b._sum.amount;
+ if (sumAmount && sumAmount > 0) {
+ youGet.push({ currency: b.currency, amount: sumAmount ?? 0 });
+ } else if (sumAmount && sumAmount < 0) {
+ youOwe.push({ currency: b.currency, amount: sumAmount ?? 0 });
+ }
+ }
+
+ return { balances, cumulatedBalances, youOwe, youGet };
+ }),
+
+ getFriends: protectedProcedure.query(async ({ ctx }) => {
+ const balanceWithFriends = await db.balance.findMany({
+ where: {
+ userId: ctx.session.user.id,
+ },
+ select: {
+ friendId: true,
+ },
+ distinct: ['friendId'],
+ });
+
+ const friendsIds = balanceWithFriends.map((f) => f.friendId);
+
+ const friends = await db.user.findMany({
+ where: {
+ id: {
+ in: friendsIds,
+ },
+ },
+ });
+
+ return friends;
+ }),
+
+ inviteFriend: protectedProcedure
+ .input(z.object({ email: z.string(), sendInviteEmail: z.boolean().optional() }))
+ .mutation(async ({ input, ctx: { session } }) => {
+ const friend = await db.user.findUnique({
+ where: {
+ email: input.email,
+ },
+ });
+
+ if (friend) {
+ console.log('Friend already exists so skipping this step');
+ return friend;
+ }
+
+ const user = await db.user.create({
+ data: {
+ email: input.email,
+ name: input.email.split('@')[0],
+ },
+ });
+
+ if (input.sendInviteEmail) {
+ sendInviteEmail(input.email, session.user.name ?? session.user.email ?? '').catch((err) => {
+ console.error('Error sending invite email', err);
+ });
+ }
+
+ return user;
+ }),
+
+ addOrEditExpense: protectedProcedure
+ .input(
+ z.object({
+ paidBy: z.number(),
+ name: z.string(),
+ category: z.string(),
+ amount: z.number(),
+ splitType: z.enum([
+ SplitType.ADJUSTMENT,
+ SplitType.EQUAL,
+ SplitType.PERCENTAGE,
+ SplitType.SHARE,
+ SplitType.EXACT,
+ SplitType.SETTLEMENT,
+ ]),
+ currency: z.string(),
+ participants: z.array(z.object({ userId: z.number(), amount: z.number() })),
+ fileKey: z.string().optional(),
+ expenseDate: z.date().optional(),
+ expenseId: z.string().optional(),
+ }),
+ )
+ .mutation(async ({ input, ctx }) => {
+ if (input.expenseId) {
+ const expenseParticipant = await db.expenseParticipant.findUnique({
+ where: {
+ expenseId_userId: {
+ expenseId: input.expenseId,
+ userId: ctx.session.user.id,
+ },
+ },
+ });
+
+ console.log('expenseParticipant', expenseParticipant);
+
+ if (!expenseParticipant) {
+ throw new TRPCError({
+ code: 'UNAUTHORIZED',
+ message: 'You are not the participant of the expense',
+ });
+ }
+ }
+
+ try {
+ const expense = input.expenseId
+ ? await editExpense(
+ input.expenseId,
+ input.paidBy,
+ input.name,
+ input.category,
+ input.amount,
+ input.splitType,
+ input.currency,
+ input.participants,
+ ctx.session.user.id,
+ input.expenseDate ?? new Date(),
+ input.fileKey,
+ )
+ : await addUserExpense(
+ input.paidBy,
+ input.name,
+ input.category,
+ input.amount,
+ input.splitType,
+ input.currency,
+ input.participants,
+ ctx.session.user.id,
+ input.expenseDate ?? new Date(),
+ input.fileKey,
+ );
+
+ return expense;
+ } catch (error) {
+ console.error(error);
+ throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Failed to create expense' });
+ }
+ }),
+
+ getExpensesWithFriend: protectedProcedure
+ .input(z.object({ friendId: z.number() }))
+ .query(async ({ input, ctx }) => {
+ const expenses = await db.expense.findMany({
+ where: {
+ OR: [
+ {
+ paidBy: ctx.session.user.id,
+ expenseParticipants: {
+ some: {
+ userId: input.friendId,
+ },
+ },
+ },
+ {
+ paidBy: input.friendId,
+ expenseParticipants: {
+ some: {
+ userId: ctx.session.user.id,
+ },
+ },
+ },
+ ],
+ AND: [
+ {
+ deletedBy: null,
+ },
+ ],
+ },
+ orderBy: {
+ expenseDate: 'desc',
+ },
+ include: {
+ expenseParticipants: {
+ where: {
+ OR: [
+ {
+ userId: ctx.session.user.id,
+ },
+ {
+ userId: input.friendId,
+ },
+ ],
+ },
+ },
+ },
+ });
+
+ return expenses;
+ }),
+
+ getBalancesWithFriend: protectedProcedure
+ .input(z.object({ friendId: z.number() }))
+ .query(async ({ input, ctx }) => {
+ const balances = db.balance.findMany({
+ where: {
+ userId: ctx.session.user.id,
+ friendId: input.friendId,
+ amount: {
+ not: 0,
+ },
+ },
+ });
+
+ return balances;
+ }),
+
+ getExpenseDetails: protectedProcedure
+ .input(z.object({ expenseId: z.string() }))
+ .query(async ({ input }) => {
+ const expense = await db.expense.findUnique({
+ where: {
+ id: input.expenseId,
+ },
+ include: {
+ expenseParticipants: {
+ include: {
+ user: true,
+ },
+ },
+ expenseNotes: true,
+ addedByUser: true,
+ paidByUser: true,
+ deletedByUser: true,
+ updatedByUser: true,
+ group: true,
+ },
+ });
+
+ return expense;
+ }),
+
+ updateUserDetail: protectedProcedure
+ .input(z.object({ name: z.string().optional(), currency: z.string().optional() }))
+ .mutation(async ({ input, ctx }) => {
+ const user = await db.user.update({
+ where: {
+ id: ctx.session.user.id,
+ },
+ data: {
+ ...input,
+ },
+ });
+
+ return user;
+ }),
+
+ getAllExpenses: protectedProcedure.query(async ({ ctx }) => {
+ const expenses = await db.expenseParticipant.findMany({
+ where: {
+ userId: ctx.session.user.id,
+ },
+ orderBy: {
+ expense: {
+ createdAt: 'desc',
+ },
+ },
+ include: {
+ expense: {
+ include: {
+ paidByUser: {
+ select: {
+ name: true,
+ email: true,
+ image: true,
+ id: true,
+ },
+ },
+ deletedByUser: {
+ select: {
+ name: true,
+ email: true,
+ image: true,
+ id: true,
+ },
+ },
+ },
+ },
+ },
+ });
+
+ return expenses;
+ }),
+
+ getUserDetails: protectedProcedure
+ .input(z.object({ userId: z.number() }))
+ .query(async ({ input }) => {
+ const user = await db.user.findUnique({
+ where: {
+ id: input.userId,
+ },
+ });
+
+ return user;
+ }),
+
+ getUploadUrl: protectedProcedure
+ .input(z.object({ fileName: z.string(), fileType: z.string(), fileSize: z.number() }))
+ .mutation(async ({ input, ctx }) => {
+ const randomId = randomUUID();
+ const extension = input.fileName.split('.').pop();
+ const key = `${ctx.session.user.id}/${randomId}.${extension}`;
+
+ if (input.fileSize > FILE_SIZE_LIMIT) {
+ throw new TRPCError({ code: 'BAD_REQUEST', message: 'File size limit exceeded' });
+ }
+
+ try {
+ const fileUrl = await getDocumentUploadUrl(key, input.fileType, input.fileSize);
+ console.log('fileUrl', fileUrl, key);
+ return { fileUrl, key };
+ } catch (e) {
+ console.error('Error getting upload url:', e);
+ throw new TRPCError({ code: 'INTERNAL_SERVER_ERROR', message: 'Error getting upload url' });
+ }
+ }),
+
+ deleteExpense: protectedProcedure
+ .input(z.object({ expenseId: z.string() }))
+ .mutation(async ({ input, ctx }) => {
+ const expenseParticipant = await db.expenseParticipant.findUnique({
+ where: {
+ expenseId_userId: {
+ expenseId: input.expenseId,
+ userId: ctx.session.user.id,
+ },
+ },
+ });
+
+ console.log('expenseParticipant', expenseParticipant);
+
+ if (!expenseParticipant) {
+ throw new TRPCError({
+ code: 'UNAUTHORIZED',
+ message: 'You are not the participant of the expense',
+ });
+ }
+
+ console.log('Deleting expense', input.expenseId);
+
+ await deleteExpense(input.expenseId, ctx.session.user.id);
+ }),
+
+ submitFeedback: protectedProcedure
+ .input(z.object({ feedback: z.string().min(10) }))
+ .mutation(async ({ input, ctx }) => {
+ await sendFeedbackEmail(input.feedback, ctx.session.user);
+ }),
+
+ getFriend: protectedProcedure
+ .input(z.object({ friendId: z.number() }))
+ .query(async ({ input, ctx }) => {
+ const friend = await db.user.findUnique({
+ where: {
+ id: input.friendId,
+ userBalances: {
+ some: {
+ friendId: ctx.session.user.id,
+ },
+ },
+ },
+ });
+
+ return friend;
+ }),
+
+ updatePushNotification: protectedProcedure
+ .input(z.object({ subscription: z.string() }))
+ .mutation(async ({ input, ctx }) => {
+ await db.pushNotification.upsert({
+ where: {
+ userId: ctx.session.user.id,
+ },
+ create: {
+ userId: ctx.session.user.id,
+ subscription: input.subscription,
+ },
+ update: {
+ subscription: input.subscription,
+ },
+ });
+ }),
+
+ deleteFriend: protectedProcedure
+ .input(z.object({ friendId: z.number() }))
+ .mutation(async ({ input, ctx }) => {
+ const friendBalances = await db.balance.findMany({
+ where: {
+ userId: ctx.session.user.id,
+ friendId: input.friendId,
+ amount: {
+ not: 0,
+ },
+ },
+ });
+
+ if (friendBalances.length > 0) {
+ throw new TRPCError({
+ code: 'BAD_REQUEST',
+ message: 'You have outstanding balances with this friend',
+ });
+ }
+
+ await db.balance.deleteMany({
+ where: {
+ userId: input.friendId,
+ friendId: ctx.session.user.id,
+ },
+ });
+
+ await db.balance.deleteMany({
+ where: {
+ friendId: input.friendId,
+ userId: ctx.session.user.id,
+ },
+ });
+ }),
+
+ downloadData: protectedProcedure.mutation(async ({ ctx }) => {
+ const user = ctx.session.user;
+
+ const friends = await getCompleteFriendsDetails(user.id);
+ const groups = await getCompleteGroupDetails(user.id);
+
+ return { friends, groups };
+ }),
+
+ importUsersFromSplitWise: protectedProcedure
+ .input(
+ z.object({
+ usersWithBalance: z.array(SplitwiseUserSchema),
+ groups: z.array(SplitwiseGroupSchema),
+ }),
+ )
+ .mutation(async ({ input, ctx }) => {
+ await importUserBalanceFromSplitWise(ctx.session.user.id, input.usersWithBalance);
+ await importGroupFromSplitwise(ctx.session.user.id, input.groups);
+ }),
+
+ getWebPushPublicKey: protectedProcedure.query(async ({ ctx }) => {
+ return env.WEB_PUSH_PUBLIC_KEY;
+ }),
+});
diff --git a/src/server/api/services/notificationService.ts b/src/server/api/services/notificationService.ts
new file mode 100644
index 0000000..7693f7c
--- /dev/null
+++ b/src/server/api/services/notificationService.ts
@@ -0,0 +1,90 @@
+import { SplitType } from '@prisma/client';
+import { db } from '~/server/db';
+import { pushNotification } from '~/server/notification';
+import { toFixedNumber } from '~/utils/numbers';
+
+export async function sendExpensePushNotification(expenseId: string) {
+ const expense = await db.expense.findUnique({
+ where: {
+ id: expenseId,
+ },
+ select: {
+ paidBy: true,
+ amount: true,
+ currency: true,
+ addedBy: true,
+ name: true,
+ deletedBy: true,
+ splitType: true,
+ deletedByUser: {
+ select: {
+ name: true,
+ email: true,
+ },
+ },
+ expenseParticipants: {
+ select: {
+ userId: true,
+ },
+ },
+ paidByUser: {
+ select: {
+ name: true,
+ email: true,
+ },
+ },
+ addedByUser: {
+ select: {
+ name: true,
+ email: true,
+ },
+ },
+ updatedByUser: {
+ select: {
+ name: true,
+ email: true,
+ },
+ },
+ },
+ });
+
+ if (!expense) {
+ return;
+ }
+
+ const participants = expense.deletedBy
+ ? expense.expenseParticipants.map((p) => p.userId).filter((e) => e !== expense.deletedBy)
+ : expense.expenseParticipants.map((p) => p.userId).filter((e) => e !== expense.addedBy);
+
+ const subscriptions = await db.pushNotification.findMany({
+ where: {
+ userId: {
+ in: participants,
+ },
+ },
+ });
+
+ const pushData = expense.deletedBy
+ ? {
+ title: `${expense.deletedByUser?.name ?? expense.deletedByUser?.email}`,
+ message: `Deleted ${expense.name}`,
+ }
+ : expense.updatedByUser
+ ? {
+ title: `${expense.updatedByUser.name ?? expense.updatedByUser.email}`,
+ message: `Updated ${expense.name} ${expense.currency} ${toFixedNumber(expense.amount)}`,
+ }
+ : expense.splitType === SplitType.SETTLEMENT
+ ? {
+ title: `${expense.addedByUser.name ?? expense.addedByUser.email}`,
+ message: `${expense.paidByUser.name ?? expense.paidByUser.email} settled up ${expense.currency} ${toFixedNumber(expense.amount)}`,
+ }
+ : {
+ title: `${expense.addedByUser.name ?? expense.addedByUser.email}`,
+ message: `${expense.paidByUser.name ?? expense.paidByUser.email} paid ${expense.currency} ${toFixedNumber(expense.amount)} for ${expense.name}`,
+ };
+
+ const pushNotifications = subscriptions.map((s) => pushNotification(s.subscription, pushData));
+
+ await Promise.all(pushNotifications);
+}
diff --git a/src/server/api/services/splitService.ts b/src/server/api/services/splitService.ts
new file mode 100644
index 0000000..2fea0a2
--- /dev/null
+++ b/src/server/api/services/splitService.ts
@@ -0,0 +1,998 @@
+import { type Expense, type SplitType, type User } from '@prisma/client';
+import { nanoid } from 'nanoid';
+import { db } from '~/server/db';
+import { type SplitwiseGroup, type SplitwiseUser } from '~/types';
+import { toFixedNumber, toInteger } from '~/utils/numbers';
+import { sendExpensePushNotification } from './notificationService';
+
+export async function joinGroup(userId: number, publicGroupId: string) {
+ const group = await db.group.findUnique({
+ where: {
+ publicId: publicGroupId,
+ },
+ });
+
+ if (!group) {
+ throw new Error('Group not found');
+ }
+
+ await db.groupUser.create({
+ data: {
+ groupId: group.id,
+ userId,
+ },
+ });
+
+ return group;
+}
+
+export async function createGroupExpense(
+ groupId: number,
+ paidBy: number,
+ name: string,
+ category: string,
+ amount: number,
+ splitType: SplitType,
+ currency: string,
+ participants: { userId: number; amount: number }[],
+ currentUserId: number,
+ expenseDate: Date,
+ fileKey?: string,
+) {
+ const operations = [];
+
+ const modifiedAmount = toInteger(amount);
+
+ // Create expense operation
+ operations.push(
+ db.expense.create({
+ data: {
+ groupId,
+ paidBy,
+ name,
+ category,
+ amount: modifiedAmount,
+ splitType,
+ currency,
+ expenseParticipants: {
+ create: participants.map((participant) => ({
+ userId: participant.userId,
+ amount: toInteger(participant.amount),
+ })),
+ },
+ fileKey,
+ addedBy: currentUserId,
+ expenseDate,
+ },
+ }),
+ );
+
+ // Update group balances and overall balances operations
+ participants.forEach((participant) => {
+ if (participant.userId === paidBy) {
+ return;
+ }
+
+ //participant.amount will be in negative
+
+ // Update balance where participant owes to the payer
+ operations.push(
+ db.groupBalance.upsert({
+ where: {
+ groupId_currency_firendId_userId: {
+ groupId,
+ currency,
+ userId: paidBy,
+ firendId: participant.userId,
+ },
+ },
+ update: {
+ amount: {
+ increment: -toInteger(participant.amount),
+ },
+ },
+ create: {
+ groupId,
+ currency,
+ userId: paidBy,
+ firendId: participant.userId,
+ amount: -toInteger(participant.amount),
+ },
+ }),
+ );
+
+ // Update balance where payer owes to the participant (opposite balance)
+ operations.push(
+ db.groupBalance.upsert({
+ where: {
+ groupId_currency_firendId_userId: {
+ groupId,
+ currency,
+ firendId: paidBy,
+ userId: participant.userId,
+ },
+ },
+ update: {
+ amount: {
+ increment: toInteger(participant.amount),
+ },
+ },
+ create: {
+ groupId,
+ currency,
+ userId: participant.userId,
+ firendId: paidBy,
+ amount: toInteger(participant.amount), // Negative because it's the opposite balance
+ },
+ }),
+ );
+
+ // Update payer's balance towards the participant
+ operations.push(
+ db.balance.upsert({
+ where: {
+ userId_currency_friendId: {
+ userId: paidBy,
+ currency,
+ friendId: participant.userId,
+ },
+ },
+ update: {
+ amount: {
+ increment: -toInteger(participant.amount),
+ },
+ },
+ create: {
+ userId: paidBy,
+ currency,
+ friendId: participant.userId,
+ amount: -toInteger(participant.amount),
+ },
+ }),
+ );
+
+ // Update participant's balance towards the payer
+ operations.push(
+ db.balance.upsert({
+ where: {
+ userId_currency_friendId: {
+ userId: participant.userId,
+ currency,
+ friendId: paidBy,
+ },
+ },
+ update: {
+ amount: {
+ increment: toInteger(participant.amount),
+ },
+ },
+ create: {
+ userId: participant.userId,
+ currency,
+ friendId: paidBy,
+ amount: toInteger(participant.amount), // Negative because it's the opposite balance
+ },
+ }),
+ );
+ });
+
+ // Execute all operations in a transaction
+ const result = await db.$transaction(operations);
+ await updateGroupExpenseForIfBalanceIsZero(
+ paidBy,
+ participants.map((p) => p.userId),
+ currency,
+ );
+ if (result[0]) {
+ sendExpensePushNotification(result[0].id).catch(console.error);
+ }
+ return result[0];
+}
+
+export async function addUserExpense(
+ paidBy: number,
+ name: string,
+ category: string,
+ amount: number,
+ splitType: SplitType,
+ currency: string,
+ participants: { userId: number; amount: number }[],
+ currentUserId: number,
+ expenseDate: Date,
+ fileKey?: string,
+) {
+ const operations = [];
+
+ // Create expense operation
+ operations.push(
+ db.expense.create({
+ data: {
+ paidBy,
+ name,
+ category,
+ amount: toInteger(amount),
+ splitType,
+ currency,
+ expenseParticipants: {
+ create: participants.map((participant) => ({
+ userId: participant.userId,
+ amount: toInteger(participant.amount),
+ })),
+ },
+ fileKey,
+ addedBy: currentUserId,
+ expenseDate,
+ },
+ }),
+ );
+
+ // Update group balances and overall balances operations
+ participants.forEach((participant) => {
+ // Update payer's balance towards the participant
+ if (participant.userId === paidBy) {
+ return;
+ }
+
+ operations.push(
+ db.balance.upsert({
+ where: {
+ userId_currency_friendId: {
+ userId: paidBy,
+ currency,
+ friendId: participant.userId,
+ },
+ },
+ update: {
+ amount: {
+ increment: -toInteger(participant.amount),
+ },
+ },
+ create: {
+ userId: paidBy,
+ currency,
+ friendId: participant.userId,
+ amount: -toInteger(participant.amount),
+ },
+ }),
+ );
+
+ // Update participant's balance towards the payer
+ operations.push(
+ db.balance.upsert({
+ where: {
+ userId_currency_friendId: {
+ userId: participant.userId,
+ currency,
+ friendId: paidBy,
+ },
+ },
+ update: {
+ amount: {
+ increment: toInteger(participant.amount),
+ },
+ },
+ create: {
+ userId: participant.userId,
+ currency,
+ friendId: paidBy,
+ amount: toInteger(participant.amount), // Negative because it's the opposite balance
+ },
+ }),
+ );
+ });
+
+ // Execute all operations in a transaction
+ const result = await db.$transaction(operations);
+ await updateGroupExpenseForIfBalanceIsZero(
+ paidBy,
+ participants.map((p) => p.userId),
+ currency,
+ );
+ if (result[0]) {
+ sendExpensePushNotification(result[0].id).catch(console.error);
+ }
+ return result[0];
+}
+
+export async function deleteExpense(expenseId: string, deletedBy: number) {
+ const expense = await db.expense.findUnique({
+ where: {
+ id: expenseId,
+ },
+ include: {
+ expenseParticipants: true,
+ },
+ });
+
+ const operations = [];
+
+ if (!expense) {
+ throw new Error('Expense not found');
+ }
+
+ for (const participant of expense.expenseParticipants) {
+ // Update payer's balance towards the participant
+ if (participant.userId === expense.paidBy) {
+ continue;
+ }
+
+ operations.push(
+ db.balance.upsert({
+ where: {
+ userId_currency_friendId: {
+ userId: expense.paidBy,
+ currency: expense.currency,
+ friendId: participant.userId,
+ },
+ },
+ create: {
+ amount: participant.amount,
+ userId: expense.paidBy,
+ currency: expense.currency,
+ friendId: participant.userId,
+ },
+ update: {
+ amount: {
+ decrement: -participant.amount,
+ },
+ },
+ }),
+ );
+
+ // Update participant's balance towards the payer
+ operations.push(
+ db.balance.upsert({
+ where: {
+ userId_currency_friendId: {
+ userId: participant.userId,
+ currency: expense.currency,
+ friendId: expense.paidBy,
+ },
+ },
+ create: {
+ amount: -participant.amount,
+ userId: participant.userId,
+ currency: expense.currency,
+ friendId: expense.paidBy,
+ },
+ update: {
+ amount: {
+ decrement: participant.amount,
+ },
+ },
+ }),
+ );
+
+ if (expense.groupId) {
+ operations.push(
+ db.groupBalance.upsert({
+ where: {
+ groupId_currency_firendId_userId: {
+ groupId: expense.groupId,
+ currency: expense.currency,
+ userId: expense.paidBy,
+ firendId: participant.userId,
+ },
+ },
+ create: {
+ amount: participant.amount,
+ groupId: expense.groupId,
+ currency: expense.currency,
+ userId: expense.paidBy,
+ firendId: participant.userId,
+ },
+ update: {
+ amount: {
+ decrement: -participant.amount,
+ },
+ },
+ }),
+ );
+
+ operations.push(
+ db.groupBalance.upsert({
+ where: {
+ groupId_currency_firendId_userId: {
+ groupId: expense.groupId,
+ currency: expense.currency,
+ userId: participant.userId,
+ firendId: expense.paidBy,
+ },
+ },
+ create: {
+ amount: -participant.amount,
+ groupId: expense.groupId,
+ currency: expense.currency,
+ userId: participant.userId,
+ firendId: expense.paidBy,
+ },
+ update: {
+ amount: {
+ decrement: participant.amount,
+ },
+ },
+ }),
+ );
+ }
+ }
+
+ operations.push(
+ db.expense.update({
+ where: { id: expenseId },
+ data: {
+ deletedBy,
+ deletedAt: new Date(),
+ },
+ }),
+ );
+
+ await db.$transaction(operations);
+ sendExpensePushNotification(expenseId).catch(console.error);
+}
+
+export async function editExpense(
+ expenseId: string,
+ paidBy: number,
+ name: string,
+ category: string,
+ amount: number,
+ splitType: SplitType,
+ currency: string,
+ participants: { userId: number; amount: number }[],
+ currentUserId: number,
+ expenseDate: Date,
+ fileKey?: string,
+) {
+ const expense = await db.expense.findUnique({
+ where: { id: expenseId },
+ include: {
+ expenseParticipants: true,
+ },
+ });
+
+ if (!expense) {
+ throw new Error('Expense not found');
+ }
+
+ const operations = [];
+
+ // First reverse all existing balances
+ for (const participant of expense.expenseParticipants) {
+ if (participant.userId === expense.paidBy) {
+ continue;
+ }
+
+ operations.push(
+ db.balance.update({
+ where: {
+ userId_currency_friendId: {
+ userId: expense.paidBy,
+ currency: expense.currency,
+ friendId: participant.userId,
+ },
+ },
+ data: {
+ amount: {
+ increment: participant.amount,
+ },
+ },
+ }),
+ );
+
+ operations.push(
+ db.balance.update({
+ where: {
+ userId_currency_friendId: {
+ userId: participant.userId,
+ currency: expense.currency,
+ friendId: expense.paidBy,
+ },
+ },
+ data: {
+ amount: {
+ decrement: participant.amount,
+ },
+ },
+ }),
+ );
+
+ // Reverse group balances if it's a group expense
+ if (expense.groupId) {
+ operations.push(
+ db.groupBalance.update({
+ where: {
+ groupId_currency_firendId_userId: {
+ groupId: expense.groupId,
+ currency: expense.currency,
+ userId: expense.paidBy,
+ firendId: participant.userId,
+ },
+ },
+ data: {
+ amount: {
+ increment: participant.amount,
+ },
+ },
+ }),
+ );
+
+ operations.push(
+ db.groupBalance.update({
+ where: {
+ groupId_currency_firendId_userId: {
+ groupId: expense.groupId,
+ currency: expense.currency,
+ userId: participant.userId,
+ firendId: expense.paidBy,
+ },
+ },
+ data: {
+ amount: {
+ decrement: participant.amount,
+ },
+ },
+ }),
+ );
+ }
+ }
+
+ // Delete existing participants
+ operations.push(
+ db.expenseParticipant.deleteMany({
+ where: {
+ expenseId,
+ },
+ }),
+ );
+
+ // Update expense with new details and create new participants
+ operations.push(
+ db.expense.update({
+ where: { id: expenseId },
+ data: {
+ paidBy,
+ name,
+ category,
+ amount: toInteger(amount),
+ splitType,
+ currency,
+ expenseParticipants: {
+ create: participants.map((participant) => ({
+ userId: participant.userId,
+ amount: toInteger(participant.amount),
+ })),
+ },
+ fileKey,
+ expenseDate,
+ updatedBy: currentUserId,
+ },
+ }),
+ );
+
+ // Add new balances
+ participants.forEach((participant) => {
+ if (participant.userId === paidBy) {
+ return;
+ }
+
+ operations.push(
+ db.balance.upsert({
+ where: {
+ userId_currency_friendId: {
+ userId: paidBy,
+ currency,
+ friendId: participant.userId,
+ },
+ },
+ create: {
+ userId: paidBy,
+ currency,
+ friendId: participant.userId,
+ amount: -toInteger(participant.amount),
+ },
+ update: {
+ amount: {
+ increment: -toInteger(participant.amount),
+ },
+ },
+ }),
+ );
+
+ operations.push(
+ db.balance.upsert({
+ where: {
+ userId_currency_friendId: {
+ userId: participant.userId,
+ currency,
+ friendId: paidBy,
+ },
+ },
+ create: {
+ userId: participant.userId,
+ currency,
+ friendId: paidBy,
+ amount: toInteger(participant.amount),
+ },
+ update: {
+ amount: {
+ increment: toInteger(participant.amount),
+ },
+ },
+ }),
+ );
+
+ // Add new group balances if it's a group expense
+ if (expense.groupId) {
+ operations.push(
+ db.groupBalance.upsert({
+ where: {
+ groupId_currency_firendId_userId: {
+ groupId: expense.groupId,
+ currency,
+ userId: paidBy,
+ firendId: participant.userId,
+ },
+ },
+ create: {
+ amount: -toInteger(participant.amount),
+ groupId: expense.groupId,
+ currency,
+ userId: paidBy,
+ firendId: participant.userId,
+ },
+ update: {
+ amount: {
+ increment: -toInteger(participant.amount),
+ },
+ },
+ }),
+ );
+
+ operations.push(
+ db.groupBalance.upsert({
+ where: {
+ groupId_currency_firendId_userId: {
+ groupId: expense.groupId,
+ currency,
+ userId: participant.userId,
+ firendId: paidBy,
+ },
+ },
+ create: {
+ amount: toInteger(participant.amount),
+ groupId: expense.groupId,
+ currency,
+ userId: participant.userId,
+ firendId: paidBy,
+ },
+ update: {
+ amount: {
+ increment: toInteger(participant.amount),
+ },
+ },
+ }),
+ );
+ }
+ });
+
+ await db.$transaction(operations);
+ await updateGroupExpenseForIfBalanceIsZero(
+ paidBy,
+ participants.map((p) => p.userId),
+ currency,
+ );
+ sendExpensePushNotification(expenseId).catch(console.error);
+ return { id: expenseId }; // Return the updated expense
+}
+
+async function updateGroupExpenseForIfBalanceIsZero(
+ userId: number,
+ friendIds: Array,
+ currency: string,
+) {
+ console.log('Checking for users with 0 balance to reflect in group');
+ const balances = await db.balance.findMany({
+ where: {
+ userId,
+ currency,
+ friendId: {
+ in: friendIds,
+ },
+ amount: 0,
+ },
+ });
+
+ console.log('Total balances needs to be updated:', balances.length);
+
+ if (balances.length) {
+ await db.groupBalance.updateMany({
+ where: {
+ userId,
+ firendId: {
+ in: friendIds,
+ },
+ currency,
+ },
+ data: {
+ amount: 0,
+ },
+ });
+
+ await db.groupBalance.updateMany({
+ where: {
+ userId: {
+ in: friendIds,
+ },
+ firendId: userId,
+ currency,
+ },
+ data: {
+ amount: 0,
+ },
+ });
+ }
+}
+
+export async function getCompleteFriendsDetails(userId: number) {
+ const balances = await db.balance.findMany({
+ where: {
+ userId,
+ },
+ include: {
+ friend: true,
+ },
+ });
+
+ const friends = balances.reduce(
+ (acc, balance) => {
+ const friendId = balance.friendId;
+ if (!acc[friendId]) {
+ acc[friendId] = {
+ balances: [],
+ id: balance.friendId,
+ email: balance.friend.email,
+ name: balance.friend.name,
+ };
+ }
+
+ if (balance.amount !== 0) {
+ acc[friendId]?.balances.push({
+ currency: balance.currency,
+ amount:
+ balance.amount > 0 ? toFixedNumber(balance.amount) : toFixedNumber(balance.amount),
+ });
+ }
+
+ return acc;
+ },
+ {} as Record<
+ number,
+ {
+ id: number;
+ email?: string | null;
+ name?: string | null;
+ balances: { currency: string; amount: number }[];
+ }
+ >,
+ );
+
+ return friends;
+}
+
+export async function getCompleteGroupDetails(userId: number) {
+ const groups = await db.group.findMany({
+ where: {
+ groupUsers: {
+ some: {
+ userId,
+ },
+ },
+ },
+ include: {
+ groupUsers: true,
+ groupBalances: true,
+ },
+ });
+
+ return groups;
+}
+
+export async function importUserBalanceFromSplitWise(
+ currentUserId: number,
+ splitWiseUsers: SplitwiseUser[],
+) {
+ const operations = [];
+
+ const users = await createUsersFromSplitwise(splitWiseUsers);
+
+ const userMap = users.reduce(
+ (acc, user) => {
+ if (user.email) {
+ acc[user.email] = user;
+ }
+
+ return acc;
+ },
+ {} as Record,
+ );
+
+ for (const user of splitWiseUsers) {
+ const dbUser = userMap[user.email];
+ if (!dbUser) {
+ continue;
+ }
+
+ for (const balance of user.balance) {
+ const amount = toInteger(parseFloat(balance.amount));
+ const currency = balance.currency_code;
+ const existingBalance = await db.balance.findUnique({
+ where: {
+ userId_currency_friendId: {
+ userId: currentUserId,
+ currency,
+ friendId: dbUser.id,
+ },
+ },
+ });
+
+ if (existingBalance?.importedFromSplitwise) {
+ continue;
+ }
+
+ operations.push(
+ db.balance.upsert({
+ where: {
+ userId_currency_friendId: {
+ userId: currentUserId,
+ currency,
+ friendId: dbUser.id,
+ },
+ },
+ update: {
+ amount: {
+ increment: amount,
+ },
+ importedFromSplitwise: true,
+ },
+ create: {
+ userId: currentUserId,
+ currency,
+ friendId: dbUser.id,
+ amount,
+ importedFromSplitwise: true,
+ },
+ }),
+ );
+
+ operations.push(
+ db.balance.upsert({
+ where: {
+ userId_currency_friendId: {
+ userId: dbUser.id,
+ currency,
+ friendId: currentUserId,
+ },
+ },
+ update: {
+ amount: {
+ increment: -amount,
+ },
+ importedFromSplitwise: true,
+ },
+ create: {
+ userId: dbUser.id,
+ currency,
+ friendId: currentUserId,
+ amount: -amount,
+ importedFromSplitwise: true,
+ },
+ }),
+ );
+ }
+ }
+
+ await db.$transaction(operations);
+}
+
+async function createUsersFromSplitwise(users: Array) {
+ const userEmails = users.map((u) => u.email);
+
+ const existingUsers = await db.user.findMany({
+ where: {
+ email: {
+ in: userEmails,
+ },
+ },
+ });
+
+ const existingUserMap: Record = {};
+
+ for (const user of existingUsers) {
+ if (user.email) {
+ existingUserMap[user.email] = true;
+ }
+ }
+
+ const newUsers = users.filter((u) => !existingUserMap[u.email]);
+
+ await db.user.createMany({
+ data: newUsers.map((u) => ({
+ email: u.email,
+ name: `${u.first_name}${u.last_name ? ' ' + u.last_name : ''}`,
+ })),
+ });
+
+ return db.user.findMany({
+ where: {
+ email: {
+ in: userEmails,
+ },
+ },
+ });
+}
+
+export async function importGroupFromSplitwise(
+ currentUserId: number,
+ splitWiseGroups: Array,
+) {
+ const splitwiseUserMap: Record = {};
+
+ for (const group of splitWiseGroups) {
+ for (const member of group.members) {
+ splitwiseUserMap[member.id.toString()] = member;
+ }
+ }
+
+ const users = await createUsersFromSplitwise(Object.values(splitwiseUserMap));
+
+ const userMap = users.reduce(
+ (acc, user) => {
+ if (user.email) {
+ acc[user.email] = user;
+ }
+
+ return acc;
+ },
+ {} as Record,
+ );
+
+ console.log('userMap', userMap, splitWiseGroups);
+
+ const operations = [];
+ console.log('Hello world');
+
+ for (const group of splitWiseGroups) {
+ console.log('group', group);
+ const dbGroup = await db.group.findUnique({
+ where: {
+ splitwiseGroupId: group.id.toString(),
+ },
+ });
+
+ if (dbGroup) {
+ continue;
+ }
+
+ const groupmembers = group.members.map((member) => ({
+ userId: userMap[member.email.toString()]!.id,
+ }));
+
+ console.log('groupmembers', groupmembers);
+
+ operations.push(
+ db.group.create({
+ data: {
+ name: group.name,
+ splitwiseGroupId: group.id.toString(),
+ publicId: nanoid(),
+ userId: currentUserId,
+ groupUsers: {
+ create: groupmembers,
+ },
+ },
+ }),
+ );
+ }
+
+ await db.$transaction(operations);
+}
diff --git a/src/server/api/trpc.ts b/src/server/api/trpc.ts
new file mode 100644
index 0000000..c055f89
--- /dev/null
+++ b/src/server/api/trpc.ts
@@ -0,0 +1,145 @@
+/**
+ * YOU PROBABLY DON'T NEED TO EDIT THIS FILE, UNLESS:
+ * 1. You want to modify request context (see Part 1).
+ * 2. You want to create a new middleware or type of procedure (see Part 3).
+ *
+ * TL;DR - This is where all the tRPC server stuff is created and plugged in. The pieces you will
+ * need to use are documented accordingly near the end.
+ */
+
+import { initTRPC, TRPCError } from '@trpc/server';
+import { type CreateNextContextOptions } from '@trpc/server/adapters/next';
+import { type Session } from 'next-auth';
+import superjson from 'superjson';
+import { z, ZodError } from 'zod';
+
+import { getServerAuthSession } from '~/server/auth';
+import { db } from '~/server/db';
+
+/**
+ * 1. CONTEXT
+ *
+ * This section defines the "contexts" that are available in the backend API.
+ *
+ * These allow you to access things when processing a request, like the database, the session, etc.
+ */
+
+interface CreateContextOptions {
+ session: Session | null;
+}
+
+/**
+ * This helper generates the "internals" for a tRPC context. If you need to use it, you can export
+ * it from here.
+ *
+ * Examples of things you may need it for:
+ * - testing, so we don't have to mock Next.js' req/res
+ * - tRPC's `createSSGHelpers`, where we don't have req/res
+ *
+ * @see https://create.t3.gg/en/usage/trpc#-serverapitrpcts
+ */
+const createInnerTRPCContext = (opts: CreateContextOptions) => {
+ return {
+ session: opts.session,
+ db,
+ };
+};
+
+/**
+ * This is the actual context you will use in your router. It will be used to process every request
+ * that goes through your tRPC endpoint.
+ *
+ * @see https://trpc.io/docs/context
+ */
+export const createTRPCContext = async (opts: CreateNextContextOptions) => {
+ const { req, res } = opts;
+
+ // Get the session from the server using the getServerSession wrapper function
+ const session = await getServerAuthSession({ req, res });
+
+ return createInnerTRPCContext({
+ session,
+ });
+};
+
+/**
+ * 2. INITIALIZATION
+ *
+ * This is where the tRPC API is initialized, connecting the context and transformer. We also parse
+ * ZodErrors so that you get typesafety on the frontend if your procedure fails due to validation
+ * errors on the backend.
+ */
+
+const t = initTRPC.context().create({
+ transformer: superjson,
+ errorFormatter({ shape, error }) {
+ return {
+ ...shape,
+ data: {
+ ...shape.data,
+ zodError: error.cause instanceof ZodError ? error.cause.flatten() : null,
+ },
+ };
+ },
+});
+
+/**
+ * 3. ROUTER & PROCEDURE (THE IMPORTANT BIT)
+ *
+ * These are the pieces you use to build your tRPC API. You should import these a lot in the
+ * "/src/server/api/routers" directory.
+ */
+
+/**
+ * This is how you create new routers and sub-routers in your tRPC API.
+ *
+ * @see https://trpc.io/docs/router
+ */
+export const createTRPCRouter = t.router;
+
+/**
+ * Public (unauthenticated) procedure
+ *
+ * This is the base piece you use to build new queries and mutations on your tRPC API. It does not
+ * guarantee that a user querying is authorized, but you can still access user session data if they
+ * are logged in.
+ */
+export const publicProcedure = t.procedure;
+
+/**
+ * Protected (authenticated) procedure
+ *
+ * If you want a query or mutation to ONLY be accessible to logged in users, use this. It verifies
+ * the session is valid and guarantees `ctx.session.user` is not null.
+ *
+ * @see https://trpc.io/docs/procedures
+ */
+export const protectedProcedure = t.procedure.use(({ ctx, next }) => {
+ if (!ctx.session || !ctx.session.user) {
+ throw new TRPCError({ code: 'UNAUTHORIZED' });
+ }
+
+ return next({
+ ctx: {
+ // infers the `session` as non-nullable
+ session: { ...ctx.session, user: ctx.session.user },
+ },
+ });
+});
+
+export const groupProcedure = protectedProcedure
+ .input(z.object({ groupId: z.number() }))
+ .use(async ({ ctx, input: { groupId }, next }) => {
+ const groupUser = await ctx.db.groupUser.findFirst({
+ where: {
+ groupId,
+ userId: ctx.session.user.id,
+ },
+ });
+
+ if (!groupUser) {
+ throw new TRPCError({ message: 'Not a group member', code: 'FORBIDDEN' });
+ }
+
+ return next({ ctx: { ...ctx } });
+ });
diff --git a/src/server/auth.ts b/src/server/auth.ts
new file mode 100644
index 0000000..e64d0a4
--- /dev/null
+++ b/src/server/auth.ts
@@ -0,0 +1,202 @@
+import { PrismaAdapter } from '@next-auth/prisma-adapter';
+import { type GetServerSidePropsContext } from 'next';
+import { getServerSession, type DefaultSession, type NextAuthOptions } from 'next-auth';
+import { Adapter, AdapterUser } from 'next-auth/adapters';
+import DiscordProvider from 'next-auth/providers/discord';
+import GoogleProvider from 'next-auth/providers/google';
+import EmailProvider from 'next-auth/providers/email';
+import AuthentikProvider from 'next-auth/providers/authentik';
+
+import { env } from '~/env';
+import { db } from '~/server/db';
+import { sendSignUpEmail } from './mailer';
+
+/**
+ * Module augmentation for `next-auth` types. Allows us to add custom properties to the `session`
+ * object and keep type safety.
+ *
+ * @see https://next-auth.js.org/getting-started/typescript#module-augmentation
+ */
+declare module 'next-auth' {
+ interface Session extends DefaultSession {
+ user: DefaultSession['user'] & {
+ id: number;
+ currency: string;
+ // ...other properties
+ // role: UserRole;
+ };
+ }
+
+ interface User {
+ id: number;
+ currency: string;
+ }
+}
+
+const SplitProPrismaAdapter = (...args: Parameters): Adapter => {
+ const prismaAdapter = PrismaAdapter(...args)
+
+ return {
+ ...prismaAdapter,
+ createUser: async (user: Omit): Promise => {
+ const prismaCreateUser = prismaAdapter.createUser
+
+ if (env.INVITE_ONLY) {
+ throw new Error("This instance is Invite Only")
+ }
+
+ if (!prismaCreateUser) {
+ // This should never happen but typing says it's possible.
+ throw new Error("Prisma Adapter lacks User Creation")
+ }
+
+ return prismaCreateUser(user)
+ },
+ }
+}
+
+
+/**
+ * Options for NextAuth.js used to configure adapters, providers, callbacks, etc.
+ *
+ * @see https://next-auth.js.org/configuration/options
+ */
+export const authOptions: NextAuthOptions = {
+ pages: {
+ signIn: '/auth/signin',
+ verifyRequest: '/auth/verify-request',
+ },
+ callbacks: {
+ session: ({ session, user }) => ({
+ ...session,
+ user: {
+ ...session.user,
+ id: user.id,
+ currency: user.currency,
+ },
+ }),
+ },
+ adapter: SplitProPrismaAdapter(db),
+ providers: getProviders(),
+ events: {
+ createUser: async ({ user }) => {
+ // Check if the user's name is empty
+ if ((!user.name || user.name.trim() === '') && user.email) {
+ // Define the logic to update the user's name here
+ const updatedName = user.email.split('@')[0];
+
+ // Use your database client to update the user's name
+ await db.user.update({
+ where: { id: user.id },
+ data: { name: updatedName },
+ });
+ }
+ },
+ },
+};
+
+/**
+ * Wrapper for `getServerSession` so that you don't need to import the `authOptions` in every file.
+ *
+ * @see https://next-auth.js.org/configuration/nextjs
+ */
+export const getServerAuthSession = (ctx: {
+ req: GetServerSidePropsContext['req'];
+ res: GetServerSidePropsContext['res'];
+}) => {
+ return getServerSession(ctx.req, ctx.res, authOptions);
+};
+
+export const getServerAuthSessionForSSG = async (context: GetServerSidePropsContext) => {
+ console.log('Before getting session');
+ const session = await getServerAuthSession(context);
+ console.log('After getting session');
+
+ if (!session?.user?.email) {
+ return {
+ redirect: {
+ destination: '/',
+ permanent: false,
+ },
+ };
+ }
+
+ return {
+ props: {
+ user: session.user,
+ },
+ };
+};
+
+/**
+ * Get providers to enable
+ */
+function getProviders() {
+ const providersList = [];
+
+ if (env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET) {
+ providersList.push(
+ GoogleProvider({
+ clientId: env.GOOGLE_CLIENT_ID,
+ clientSecret: env.GOOGLE_CLIENT_SECRET,
+ allowDangerousEmailAccountLinking: true,
+ }),
+ );
+ }
+
+ if (env.EMAIL_SERVER_HOST) {
+ providersList.push(
+ EmailProvider({
+ from: env.FROM_EMAIL,
+ server: {
+ host: env.EMAIL_SERVER_HOST,
+ port: parseInt(env.EMAIL_SERVER_PORT ?? ''),
+ auth: {
+ user: env.EMAIL_SERVER_USER,
+ pass: env.EMAIL_SERVER_PASSWORD,
+ },
+ },
+ async sendVerificationRequest({ identifier: email, url, token }) {
+ const result = await sendSignUpEmail(email, token, url);
+ if (!result) {
+ throw new Error('Failed to send email');
+ }
+ },
+ async generateVerificationToken() {
+ return Math.random().toString(36).substring(2, 7).toLowerCase();
+ },
+ }),
+ );
+ }
+
+ if (env.AUTHENTIK_ID && env.AUTHENTIK_SECRET && env.AUTHENTIK_ISSUER) {
+ providersList.push(
+ AuthentikProvider({
+ clientId: env.AUTHENTIK_ID,
+ clientSecret: env.AUTHENTIK_SECRET,
+ issuer: env.AUTHENTIK_ISSUER,
+ allowDangerousEmailAccountLinking: true,
+ })
+ );
+ }
+
+ return providersList;
+}
+
+/**
+ * Validates the environment variables that are related to authentication.
+ * this will check if atleat one provider is set properly.
+ *
+ * this function should be updated if new providers are added.
+ */
+export function validateAuthEnv() {
+ console.log('Validating auth env');
+ if (!process.env.SKIP_ENV_VALIDATION) {
+ const providers = getProviders();
+ if (providers.length === 0) {
+ throw new Error(
+ 'No authentication providers are configured, at least one is required. Learn more here: https://github.com/oss-apps/split-pro?tab=readme-ov-file#setting-up-the-environment',
+ );
+ }
+ }
+}
diff --git a/src/server/db.ts b/src/server/db.ts
new file mode 100644
index 0000000..678a0e0
--- /dev/null
+++ b/src/server/db.ts
@@ -0,0 +1,15 @@
+import { PrismaClient } from '@prisma/client';
+
+import { env } from '~/env';
+
+const globalForPrisma = globalThis as unknown as {
+ prisma: PrismaClient | undefined;
+};
+
+export const db =
+ globalForPrisma.prisma ??
+ new PrismaClient({
+ log: env.NODE_ENV === 'development' ? ['query', 'error', 'warn'] : ['error'],
+ });
+
+if (env.NODE_ENV !== 'production') globalForPrisma.prisma = db;
diff --git a/src/server/mailer.ts b/src/server/mailer.ts
new file mode 100644
index 0000000..c0be5d7
--- /dev/null
+++ b/src/server/mailer.ts
@@ -0,0 +1,120 @@
+import { type User } from 'next-auth';
+import nodemailer, { type Transporter } from 'nodemailer';
+import { env } from '~/env';
+import { sendToDiscord } from './service-notification';
+
+let transporter: Transporter;
+
+const getTransporter = () => {
+ if (transporter) return transporter;
+
+ const host = env.EMAIL_SERVER_HOST;
+ const port = parseInt(env.EMAIL_SERVER_PORT ?? '');
+ const user = env.EMAIL_SERVER_USER;
+ const pass = env.EMAIL_SERVER_PASSWORD;
+
+ if (!host) {
+ return;
+ }
+
+ const transport = {
+ host,
+ secure: port === 465,
+ port,
+ auth: {
+ user,
+ pass,
+ },
+ tls: {
+ rejectUnauthorized: env.NODE_ENV !== 'development',
+ },
+ };
+
+ transporter = nodemailer.createTransport(transport);
+ return transporter;
+};
+
+export async function sendSignUpEmail(email: string, token: string, url: string) {
+ const { host } = new URL(url);
+
+ console.log(env.NODE_ENV);
+
+ if (env.NODE_ENV === 'development') {
+ console.log('Sign in link : ', email, url, token);
+ return true;
+ }
+
+ const subject = 'Sign in to SplitPro';
+ const text = `Hey,\n\nYou can sign in to SplitPro by clicking the below URL:\n${url}\n\nYou can also use this OTP: ${token}\n\nThanks,\nSplitPro Team`;
+ const html = `Hey,
You can sign in to SplitPro by clicking the below URL:
Sign in to ${host}
You can also use this OTP: ${token}
Thanks,
SplitPro Team`;
+
+ return await sendMail(email, subject, text, html);
+}
+
+export async function sendInviteEmail(email: string, name: string) {
+ if (!env.ENABLE_SENDING_INVITES) {
+ throw new Error("Sending invites is not enabled")
+ }
+
+ const { host } = new URL(env.NEXTAUTH_URL);
+
+ if (env.NODE_ENV === 'development') {
+ console.log('Sending invite email', email, name);
+ return;
+ }
+
+ const subject = 'Invitation to SplitPro';
+ const text = `Hey,\n\nYou have been invited to SplitPro by ${name}. It's a completely open source free alternative to splitwise. You can sign in to SplitPro by clicking the below URL:\n${env.NEXTAUTH_URL}\n\nThanks,\nSplitPro Team`;
+ const html = `Hey,
You have been invited to SplitPro by ${name}. It's a completely open source free alternative to splitwise. You can sign in to SplitPro by clicking the below URL:
Sign in to ${host}
Thanks, SplitPro Team
`;
+
+ await sendMail(email, subject, text, html);
+}
+
+export async function sendFeedbackEmail(feedback: string, user: User) {
+ console.log('Received feedback from: ', user.email, 'Feedback: ', feedback);
+
+ if (!env.FEEDBACK_EMAIL) return;
+
+ const subject = `Feedback received on SplitPro from ${user.name}`;
+ const text = `Feedback created by ${user.name} :\n\nFeedback: ${feedback}\n\nemail: ${user.email}`;
+
+ await sendMail(env.FEEDBACK_EMAIL, subject, text, text, user.email ?? undefined);
+}
+
+async function sendMail(
+ email: string,
+ subject: string,
+ text: string,
+ html: string,
+ replyTo?: string,
+) {
+ const transporter = getTransporter();
+ try {
+ if (transporter) {
+ await transporter.sendMail({
+ to: email,
+ from: env.FROM_EMAIL,
+ subject,
+ text,
+ html,
+ replyTo,
+ });
+
+ console.log('Email sent');
+ return true;
+ } else {
+ console.log('SMTP server not configured, so skipping');
+ }
+ } catch (error) {
+ console.log('Error sending email', error);
+ await sendToDiscord(
+ `Error sending email: ${
+ error instanceof Error
+ ? `error.message: ${error.message}\nerror.stack: ${error.stack}`
+ : 'Unknown error'
+ }`,
+ );
+ }
+
+ return false;
+}
diff --git a/src/server/notification.ts b/src/server/notification.ts
new file mode 100644
index 0000000..a6edb81
--- /dev/null
+++ b/src/server/notification.ts
@@ -0,0 +1,23 @@
+import webPush, { type PushSubscription } from 'web-push';
+import { env } from '~/env';
+
+if (env.WEB_PUSH_EMAIL && env.WEB_PUSH_PUBLIC_KEY && env.WEB_PUSH_PRIVATE_KEY) {
+ webPush.setVapidDetails(
+ `mailto:${env.WEB_PUSH_EMAIL}`,
+ env.WEB_PUSH_PUBLIC_KEY,
+ env.WEB_PUSH_PRIVATE_KEY,
+ );
+}
+
+export async function pushNotification(
+ subscription: string,
+ message: { title: string; message: string },
+) {
+ try {
+ const _subscription = JSON.parse(subscription) as PushSubscription;
+ const response = await webPush.sendNotification(_subscription, JSON.stringify(message));
+ console.log('Push notification response', response);
+ } catch (error) {
+ console.error('Error sending push notification', error);
+ }
+}
diff --git a/src/server/service-notification.ts b/src/server/service-notification.ts
new file mode 100644
index 0000000..3409067
--- /dev/null
+++ b/src/server/service-notification.ts
@@ -0,0 +1,28 @@
+import { env } from '~/env';
+
+export async function sendToDiscord(message: string) {
+ if (!env.DISCORD_WEBHOOK_URL) {
+ console.error(
+ 'Discord webhook URL is not defined in the environment variables. So printing the message to the console.',
+ );
+ console.log('Message: ', message);
+ return;
+ }
+
+ const webhookUrl = env.DISCORD_WEBHOOK_URL;
+ const response = await fetch(webhookUrl, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({ content: message }),
+ });
+
+ if (response.ok) {
+ console.log('Message sent to Discord successfully.');
+ } else {
+ console.error('Failed to send message to Discord:', response.statusText);
+ }
+
+ return;
+}
diff --git a/src/server/storage.ts b/src/server/storage.ts
new file mode 100644
index 0000000..51514c3
--- /dev/null
+++ b/src/server/storage.ts
@@ -0,0 +1,46 @@
+import { S3Client, PutObjectCommand } from '@aws-sdk/client-s3';
+import { getSignedUrl } from '@aws-sdk/s3-request-presigner';
+import { env } from '~/env';
+
+let S3: S3Client | null = null;
+
+export const isStorageConfigured = () =>
+ env.R2_URL && env.R2_ACCESS_KEY && env.R2_SECRET_KEY && env.R2_BUCKET;
+
+// console.log(isStorageConfigured());
+
+const getClient = () => {
+ if (!S3 && env.R2_URL && env.R2_ACCESS_KEY && env.R2_SECRET_KEY && env.R2_BUCKET) {
+ S3 = new S3Client({
+ region: 'auto',
+ endpoint: env.R2_URL,
+ credentials: {
+ accessKeyId: env.R2_ACCESS_KEY,
+ secretAccessKey: env.R2_SECRET_KEY,
+ },
+ forcePathStyle: true, // needed for minio
+ });
+ }
+
+ return S3;
+};
+
+export const getDocumentUploadUrl = async (key: string, fileType: string, fileSize?: number) => {
+ const s3Client = getClient();
+
+ if (!s3Client) {
+ throw new Error('R2 is not configured');
+ }
+
+ const url = await getSignedUrl(
+ s3Client,
+ new PutObjectCommand({
+ Bucket: env.R2_BUCKET,
+ Key: key,
+ ContentType: fileType,
+ }),
+ { expiresIn: 3600 },
+ );
+
+ return url;
+};
diff --git a/src/store/addStore.ts b/src/store/addStore.ts
new file mode 100644
index 0000000..94dffa2
--- /dev/null
+++ b/src/store/addStore.ts
@@ -0,0 +1,339 @@
+import { type Group, SplitType, type User } from '@prisma/client';
+import Router from 'next/router';
+import { create } from 'zustand';
+
+export type Participant = User & { amount?: number; splitShare?: number };
+
+interface AddExpenseState {
+ amount: number;
+ amountStr: string;
+ currentUser: User | undefined;
+ splitType: SplitType;
+ group: Group | undefined;
+ participants: Array;
+ description: string;
+ currency: string;
+ category: string;
+ nameOrEmail: string;
+ paidBy: User | undefined;
+ showFriends: boolean;
+ isFileUploading: boolean;
+ fileKey?: string;
+ canSplitScreenClosed: boolean;
+ splitScreenOpen: boolean;
+ expenseDate: Date | undefined;
+ actions: {
+ setAmount: (amount: number) => void;
+ setAmountStr: (amountStr: string) => void;
+ setSplitType: (splitType: SplitType) => void;
+ setGroup: (group: Group | undefined) => void;
+ addOrUpdateParticipant: (user: Participant) => void;
+ setParticipants: (participants: Array) => void;
+ removeParticipant: (userId: number) => void;
+ removeLastParticipant: () => void;
+ setCurrency: (currency: string) => void;
+ setCategory: (category: string) => void;
+ setNameOrEmail: (nameOrEmail: string) => void;
+ setPaidBy: (user: User) => void;
+ setCurrentUser: (user: User) => void;
+ setDescription: (description: string) => void;
+ setFileUploading: (isFileUploading: boolean) => void;
+ setFileKey: (fileKey: string) => void;
+ resetState: () => void;
+ setSplitScreenOpen: (splitScreenOpen: boolean) => void;
+ setExpenseDate: (expenseDate: Date | undefined) => void;
+ };
+}
+
+export const useAddExpenseStore = create()((set) => ({
+ amount: 0,
+ amountStr: '',
+ splitType: SplitType.EQUAL,
+ group: undefined,
+ participants: [],
+ currency: 'USD',
+ category: 'general',
+ nameOrEmail: '',
+ paidBy: undefined,
+ currentUser: undefined,
+ description: '',
+ showFriends: true,
+ isFileUploading: false,
+ fileKey: undefined,
+ canSplitScreenClosed: true,
+ splitScreenOpen: false,
+ expenseDate: undefined,
+ actions: {
+ setAmount: (amount) =>
+ set((s) => {
+ const { participants, canSplitScreenClosed } = calculateParticipantSplit(
+ amount,
+ s.participants,
+ s.splitType,
+ s.paidBy,
+ );
+
+ return { amount, participants, canSplitScreenClosed };
+ }),
+ setAmountStr: (amountStr) => set({ amountStr }),
+ setSplitType: (splitType) =>
+ set((state) => {
+ return {
+ splitType,
+ ...calculateParticipantSplit(state.amount, state.participants, splitType, state.paidBy),
+ };
+ }),
+ setGroup: (group) => {
+ set({ group });
+ },
+ addOrUpdateParticipant: (user) =>
+ set((state) => {
+ const participants = [...state.participants];
+ const userIndex = participants.findIndex((p) => p.id === user.id);
+ if (userIndex !== -1) {
+ participants[userIndex] = user;
+ } else {
+ participants.push({ ...user, splitShare: state.splitType === SplitType.EQUAL ? 1 : 0 });
+ }
+ return {
+ ...calculateParticipantSplit(state.amount, participants, state.splitType, state.paidBy),
+ };
+ }),
+ setParticipants: (_participants) =>
+ set((state) => {
+ const participants = _participants.map((p) => ({
+ splitShare: state.splitType === SplitType.EQUAL ? 1 : 0,
+ ...p,
+ }));
+ return {
+ splitType: SplitType.EQUAL,
+ ...calculateParticipantSplit(state.amount, participants, SplitType.EQUAL, state.paidBy),
+ };
+ }),
+ removeLastParticipant: () => {
+ set((state) => {
+ const currentPath = window.location.pathname;
+ const searchParams = new URLSearchParams(window.location.search);
+ searchParams.delete('friendId');
+
+ Router.push(`${currentPath}?${searchParams.toString()}`).catch(console.error);
+
+ if (state.participants.length === 1) return {};
+ const newParticipants = [...state.participants];
+ newParticipants.pop();
+ return {
+ ...calculateParticipantSplit(
+ state.amount,
+ newParticipants,
+ state.splitType,
+ state.paidBy,
+ ),
+ };
+ });
+ },
+ removeParticipant: (userId) => {
+ set((state) => {
+ const currentPath = window.location.pathname;
+ const searchParams = new URLSearchParams(window.location.search);
+ searchParams.delete('friendId');
+
+ Router.push(`${currentPath}?${searchParams.toString()}`).catch(console.error);
+
+ const newParticipants = state.participants.filter((p) => p.id !== userId);
+ return {
+ ...calculateParticipantSplit(
+ state.amount,
+ newParticipants,
+ state.splitType,
+ state.paidBy,
+ ),
+ };
+ });
+ },
+ setCurrency: (currency) => set({ currency }),
+ setCategory: (category) => set({ category }),
+ setNameOrEmail: (nameOrEmail) => set({ nameOrEmail, showFriends: nameOrEmail.length > 0 }),
+ setPaidBy: (paidBy) =>
+ set((s) => ({
+ paidBy,
+ ...calculateParticipantSplit(s.amount, s.participants, s.splitType, paidBy),
+ })),
+ setCurrentUser: (currentUser) =>
+ set((s) => {
+ const cUser = s.participants.find((p) => p.id === currentUser.id);
+ const participants = [...s.participants];
+
+ if (!cUser) {
+ participants.push(currentUser);
+ }
+ return { currentUser, paidBy: currentUser, participants };
+ }),
+ setDescription: (description) => set({ description }),
+ setFileUploading: (isFileUploading) => set({ isFileUploading }),
+ setFileKey: (fileKey) => set({ fileKey }),
+ resetState: () => {
+ set((s) => ({
+ amount: 0,
+ participants: s.currentUser ? [s.currentUser] : [],
+ description: '',
+ fileKey: '',
+ category: 'general',
+ splitType: SplitType.EQUAL,
+ group: undefined,
+ amountStr: '',
+ }));
+ },
+ setSplitScreenOpen: (splitScreenOpen) => set({ splitScreenOpen }),
+ setExpenseDate: (expenseDate) => set({ expenseDate }),
+ },
+}));
+
+export function calculateParticipantSplit(
+ amount: number,
+ participants: Array,
+ splitType: SplitType,
+ paidBy?: User,
+) {
+ let canSplitScreenClosed = true;
+ if (amount === 0) return { participants, canSplitScreenClosed };
+
+ let updatedParticipants = participants;
+
+ switch (splitType) {
+ case SplitType.EQUAL:
+ const totalParticipants = participants.filter((p) => p.splitShare !== 0).length;
+ updatedParticipants = participants.map((p) => ({
+ ...p,
+ amount: p.splitShare === 0 ? 0 : amount / totalParticipants,
+ }));
+ canSplitScreenClosed = !!updatedParticipants.find((p) => p.splitShare);
+ break;
+ case SplitType.PERCENTAGE:
+ updatedParticipants = participants.map((p) => ({
+ ...p,
+ amount: ((p.splitShare ?? 0) / 100) * amount,
+ }));
+
+ canSplitScreenClosed =
+ 100 - participants.reduce((acc, p) => acc + (p.splitShare ?? 0), 0) === 0;
+ break;
+ case SplitType.SHARE:
+ const totalShare = participants.reduce((acc, p) => acc + (p.splitShare ?? 0), 0);
+ updatedParticipants = participants.map((p) => ({
+ ...p,
+ amount: ((p.splitShare ?? 0) * amount) / totalShare,
+ }));
+ break;
+ case SplitType.EXACT:
+ const totalSplitShare = participants.reduce((acc, p) => acc + (p.splitShare ?? 0), 0);
+
+ const epsilon = 0.01;
+ canSplitScreenClosed = Math.abs(amount - totalSplitShare) < epsilon;
+
+ updatedParticipants = participants.map((p) => ({ ...p, amount: p.splitShare ?? 0 }));
+ break;
+ case SplitType.ADJUSTMENT:
+ const totalAdjustment = participants.reduce((acc, p) => acc + (p.splitShare ?? 0), 0);
+ if (totalAdjustment > amount) {
+ canSplitScreenClosed = false;
+ }
+ const remainingAmountShare = (amount - totalAdjustment) / participants.length;
+ updatedParticipants = participants.map((p) => ({
+ ...p,
+ amount: remainingAmountShare + (p.splitShare ?? 0),
+ }));
+ break;
+ }
+
+ updatedParticipants = updatedParticipants.map((p) => {
+ if (p.id === paidBy?.id) {
+ return { ...p, amount: -(p.amount ?? 0) + amount };
+ }
+ return { ...p, amount: -(p.amount ?? 0) };
+ });
+
+ return { participants: updatedParticipants, canSplitScreenClosed };
+}
+
+export function calculateSplitShareBasedOnAmount(
+ amount: number,
+ participants: Array,
+ splitType: SplitType,
+ paidBy?: User,
+) {
+ let updatedParticipants = [...participants];
+
+ console.log('calculateSplitShareBasedOnAmount', amount, participants, splitType);
+
+ switch (splitType) {
+ case SplitType.EQUAL:
+ // For equal split, split share should be amount/participants or 0 if amount is 0
+ updatedParticipants = participants.map((p) => ({
+ ...p,
+ splitShare: (paidBy?.id === p.id ? (p.amount ?? 0) - amount : p.amount) === 0 ? 0 : 1,
+ }));
+ break;
+
+ case SplitType.PERCENTAGE:
+ // Convert amounts back to percentages
+ updatedParticipants = participants.map((p) => ({
+ ...p,
+ splitShare:
+ amount === 0
+ ? 0
+ : paidBy?.id !== p.id
+ ? (Math.abs(p.amount ?? 0) / amount) * 100
+ : (Math.abs(amount - (p.amount ?? 0)) / amount) * 100,
+ }));
+ break;
+
+ case SplitType.SHARE:
+ // Convert amounts back to shares
+ const shares = participants.map((p) =>
+ p.id === paidBy?.id
+ ? Math.abs(amount - (p.amount ?? 0)) / amount
+ : Math.abs(p.amount ?? 0) / amount,
+ );
+
+ // Find the minimum share value
+ const minShare = Math.min(...shares);
+
+ // Calculate multiplier to make minimum share equal to 1
+ const multiplier = minShare !== 0 ? 1 / minShare : 1;
+
+ updatedParticipants = participants.map((p) => ({
+ ...p,
+ splitShare:
+ (amount === 0
+ ? 0
+ : paidBy?.id !== p.id
+ ? Math.abs(p.amount ?? 0) / amount
+ : Math.abs(amount - (p.amount ?? 0)) / amount) * multiplier,
+ }));
+ break;
+
+ case SplitType.EXACT:
+ // For exact, split share is the absolute amount
+ updatedParticipants = participants.map((p) => ({
+ ...p,
+ splitShare:
+ paidBy?.id !== p.id ? Math.abs(p.amount ?? 0) : Math.abs(amount - (p.amount ?? 0)),
+ }));
+ break;
+
+ case SplitType.ADJUSTMENT:
+ // For adjustment, split share is the difference from equal share
+ updatedParticipants = participants.map((p) => ({
+ ...p,
+ splitShare:
+ amount === 0
+ ? 0
+ : paidBy?.id !== p.id
+ ? Math.abs(p.amount ?? 0)
+ : Math.abs(amount - (p.amount ?? 0)),
+ }));
+ break;
+ }
+
+ return updatedParticipants;
+}
diff --git a/src/store/appStore.ts b/src/store/appStore.ts
new file mode 100644
index 0000000..f207dc1
--- /dev/null
+++ b/src/store/appStore.ts
@@ -0,0 +1,15 @@
+import { create } from 'zustand';
+
+interface AppState {
+ webPushPublicKey: string | null;
+ actions: {
+ setWebPushPublicKey: (key: string) => void;
+ };
+}
+
+export const useAppStore = create()((set) => ({
+ webPushPublicKey: null,
+ actions: {
+ setWebPushPublicKey: (key) => set({ webPushPublicKey: key }),
+ },
+}));
diff --git a/src/styles/globals.css b/src/styles/globals.css
new file mode 100644
index 0000000..19c1043
--- /dev/null
+++ b/src/styles/globals.css
@@ -0,0 +1,180 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer base {
+ :root {
+ --background: 0 0% 100%;
+ --foreground: 224 71.4% 4.1%;
+
+ --card: 0 0% 100%;
+ --card-foreground: 224 71.4% 4.1%;
+
+ --popover: 0 0% 100%;
+ --popover-foreground: 224 71.4% 4.1%;
+
+ --primary: 220.9 39.3% 11%;
+ --primary-foreground: 210 20% 98%;
+
+ --secondary: 220 14.3% 95.9%;
+ --secondary-foreground: 220.9 39.3% 11%;
+
+ --muted: 220 14.3% 95.9%;
+ --muted-foreground: 220 8.9% 46.1%;
+
+ --accent: 220 14.3% 95.9%;
+ --accent-foreground: 220.9 39.3% 11%;
+
+ --destructive: 0 84.2% 60.2%;
+ --destructive-foreground: 210 20% 98%;
+
+ --border: 220 13% 91%;
+ --input: 220 13% 91%;
+ --ring: 224 71.4% 4.1%;
+
+ --radius: 0.5rem;
+ }
+
+ .dark {
+ --background: 224 71.4% 4.1%;
+ --foreground: 222 2% 83%;
+
+ --card: 224 71.4% 4.1%;
+ --card-foreground: 210 20% 98%;
+
+ --popover: 224 71.4% 4.1%;
+ --popover-foreground: 210 20% 98%;
+
+ --primary: 189 94% 43%;
+ --primary-foreground: 220.9 39.3% 11%;
+
+ --secondary: 215 27.9% 16.9%;
+ --secondary-foreground: 210 20% 98%;
+
+ --muted: 215 27.9% 16.9%;
+ --muted-foreground: 217.9 10.6% 64.9%;
+
+ --accent: 215 27.9% 16.9%;
+ --accent-foreground: 210 20% 98%;
+
+ --destructive: 0 74% 42%;
+ --destructive-foreground: 210 20% 98%;
+
+ --border: 215 27.9% 16.9%;
+ --input: 215 27.9% 16.9%;
+ --ring: 221 39% 11%;
+ }
+}
+
+@layer base {
+ * {
+ @apply border-border;
+ }
+ body {
+ @apply h-full bg-background text-foreground;
+ }
+}
+
+html {
+ @apply h-full bg-background;
+}
+
+body {
+ @apply h-full;
+}
+
+#__next {
+ @apply h-full;
+}
+
+input::-webkit-inner-spin-button {
+ -webkit-appearance: none;
+}
+
+input[type="number"] {
+ -moz-appearance: textfield;
+}
+
+/* Make clicks pass-through */
+#nprogress {
+ pointer-events: none;
+}
+
+#nprogress .bar {
+ @apply bg-primary;
+
+ position: fixed;
+ z-index: 1031;
+ top: 0;
+ left: 0;
+
+ width: 100%;
+ height: 1px;
+}
+
+/* Fancy blur effect */
+#nprogress .peg {
+ display: block;
+ position: absolute;
+ right: 0px;
+ width: 100px;
+ height: 100%;
+ box-shadow:
+ 0 0 10px #29d,
+ 0 0 5px #29d;
+ opacity: 1;
+
+ -webkit-transform: rotate(3deg) translate(0px, -4px);
+ -ms-transform: rotate(3deg) translate(0px, -4px);
+ transform: rotate(3deg) translate(0px, -4px);
+}
+
+/* Remove these to get rid of the spinner */
+#nprogress .spinner {
+ display: block;
+ position: fixed;
+ z-index: 1031;
+ top: 15px;
+ right: 15px;
+}
+
+#nprogress .spinner-icon {
+ width: 18px;
+ height: 18px;
+ box-sizing: border-box;
+
+ border: solid 2px transparent;
+ border-top-color: #29d;
+ border-left-color: #29d;
+ border-radius: 50%;
+
+ -webkit-animation: nprogress-spinner 400ms linear infinite;
+ animation: nprogress-spinner 400ms linear infinite;
+}
+
+.nprogress-custom-parent {
+ overflow: hidden;
+ position: relative;
+}
+
+.nprogress-custom-parent #nprogress .spinner,
+.nprogress-custom-parent #nprogress .bar {
+ position: absolute;
+}
+
+@-webkit-keyframes nprogress-spinner {
+ 0% {
+ -webkit-transform: rotate(0deg);
+ }
+ 100% {
+ -webkit-transform: rotate(360deg);
+ }
+}
+@keyframes nprogress-spinner {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+}
diff --git a/src/types.ts b/src/types.ts
new file mode 100644
index 0000000..2ade53d
--- /dev/null
+++ b/src/types.ts
@@ -0,0 +1,60 @@
+import { type NextPage } from 'next';
+import { type User } from 'next-auth';
+import { z } from 'zod';
+
+// eslint-disable-next-line @typescript-eslint/ban-types
+export type NextPageWithUser = NextPage<{ user: User } & T> & { auth: boolean };
+
+export type PushMessage = { title: string; message: string };
+
+export type SplitwisePicture = {
+ small: string;
+ medium: string;
+ large: string;
+};
+
+export type SplitwiseBalance = {
+ currency_code: string;
+ amount: string;
+};
+
+export type SplitwiseUser = {
+ id: number;
+ first_name: string;
+ last_name: string | null;
+ email: string;
+ balance: SplitwiseBalance[];
+ picture: SplitwisePicture;
+};
+
+export type SplitwiseGroup = {
+ id: number;
+ name: string;
+ members: SplitwiseUser[];
+};
+
+const SplitwisePictureSchema = z.object({
+ small: z.string(),
+ medium: z.string(),
+ large: z.string(),
+});
+
+const SplitwiseBalanceSchema = z.object({
+ currency_code: z.string(),
+ amount: z.string(),
+});
+
+export const SplitwiseUserSchema = z.object({
+ id: z.number(),
+ first_name: z.string(),
+ last_name: z.string().nullable(),
+ email: z.string().email(),
+ balance: z.array(SplitwiseBalanceSchema),
+ picture: SplitwisePictureSchema,
+});
+
+export const SplitwiseGroupSchema = z.object({
+ id: z.number(),
+ name: z.string(),
+ members: z.array(SplitwiseUserSchema),
+});
diff --git a/src/types/service-worker.d.ts b/src/types/service-worker.d.ts
new file mode 100644
index 0000000..d962098
--- /dev/null
+++ b/src/types/service-worker.d.ts
@@ -0,0 +1,130 @@
+/**
+ * Copyright (c) 2016, Tiernan Cridland
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any purpose with or without
+ * fee is hereby
+ * granted, provided that the above copyright notice and this permission notice appear in all
+ * copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT,
+ * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER
+ * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Typings for Service Worker
+ * @author Tiernan Cridland
+ * @email tiernanc@gmail.com
+ * @license: ISC
+ */
+interface ExtendableEvent extends Event {
+ waitUntil(fn: Promise): void;
+}
+
+interface PushSubscriptionChangeEvent extends ExtendableEvent {
+ readonly newSubscription?: PushSubscription;
+ readonly oldSubscription?: PushSubscription;
+}
+
+// Client API
+
+declare class Client {
+ frameType: ClientFrameType;
+ id: string;
+ url: string;
+ focused: boolean;
+ focus(): void;
+ postMessage(message: unknown): void;
+}
+
+interface Clients {
+ claim(): Promise;
+ get(id: string): Promise;
+ matchAll(options?: ClientMatchOptions): Promise>;
+ openWindow(url: string): Promise;
+}
+
+interface ClientMatchOptions {
+ includeUncontrolled?: boolean;
+ type?: ClientMatchTypes;
+}
+
+interface WindowClient {
+ focused: boolean;
+ visibilityState: WindowClientState;
+ focus(): Promise;
+ navigate(url: string): Promise;
+}
+
+type ClientFrameType = 'auxiliary' | 'top-level' | 'nested' | 'none';
+type ClientMatchTypes = 'window' | 'worker' | 'sharedworker' | 'all';
+type WindowClientState = 'hidden' | 'visible' | 'prerender' | 'unloaded';
+
+// Fetch API
+
+interface FetchEvent extends ExtendableEvent {
+ clientId: string | null;
+ request: Request;
+ respondWith(response: Promise | Response): Promise;
+}
+
+interface InstallEvent extends ExtendableEvent {
+ activeWorker: ServiceWorker;
+}
+
+type ActivateEvent = ExtendableEvent;
+
+// Notification API
+
+interface NotificationEvent extends ExtendableEvent {
+ action: string;
+ notification: Notification;
+}
+
+// Push API
+
+interface PushEvent extends ExtendableEvent {
+ data: PushMessageData;
+}
+
+interface PushMessageData {
+ arrayBuffer(): ArrayBuffer;
+ blob(): Blob;
+ json(): Record;
+ text(): string;
+}
+
+// Sync API
+
+interface SyncEvent extends ExtendableEvent {
+ lastChance: boolean;
+ tag: string;
+}
+
+interface ExtendableMessageEvent extends ExtendableEvent {
+ data: unknown;
+ source: Client | object;
+}
+
+// ServiceWorkerGlobalScope
+
+interface ServiceWorkerGlobalScope {
+ caches: CacheStorage;
+ clients: Clients;
+ registration: ServiceWorkerRegistration;
+
+ addEventListener(event: 'activate', fn: (event?: ExtendableEvent) => unknown): void;
+ addEventListener(event: 'message', fn: (event?: ExtendableMessageEvent) => unknown): void;
+ addEventListener(event: 'fetch', fn: (event?: FetchEvent) => unknown): void;
+ addEventListener(event: 'install', fn: (event?: ExtendableEvent) => unknown): void;
+ addEventListener(event: 'push', fn: (event?: PushEvent) => unknown): void;
+ addEventListener(event: 'notificationclick', fn: (event?: NotificationEvent) => unknown): void;
+ addEventListener(event: 'sync', fn: (event?: SyncEvent) => unknown): void;
+
+ fetch(request: Request | string): Promise;
+ skipWaiting(): Promise;
+}
diff --git a/src/utils/api.ts b/src/utils/api.ts
new file mode 100644
index 0000000..b1eff0b
--- /dev/null
+++ b/src/utils/api.ts
@@ -0,0 +1,68 @@
+/**
+ * This is the client-side entrypoint for your tRPC API. It is used to create the `api` object which
+ * contains the Next.js App-wrapper, as well as your type-safe React Query hooks.
+ *
+ * We also create a few inference helpers for input and output types.
+ */
+import { httpBatchLink, loggerLink } from '@trpc/client';
+import { createTRPCNext } from '@trpc/next';
+import { type inferRouterInputs, type inferRouterOutputs } from '@trpc/server';
+import superjson from 'superjson';
+
+import { type AppRouter } from '~/server/api/root';
+
+const getBaseUrl = () => {
+ if (typeof window !== 'undefined') return ''; // browser should use relative url
+ if (process.env.VERCEL_URL) return `https://${process.env.VERCEL_URL}`; // SSR should use vercel url
+ return `http://localhost:${process.env.PORT ?? 3000}`; // dev SSR should use localhost
+};
+
+/** A set of type-safe react-query hooks for your tRPC API. */
+export const api = createTRPCNext({
+ config() {
+ return {
+ /**
+ * Transformer used for data de-serialization from the server.
+ *
+ * @see https://trpc.io/docs/data-transformers
+ */
+ transformer: superjson,
+
+ /**
+ * Links used to determine request flow from client to server.
+ *
+ * @see https://trpc.io/docs/links
+ */
+ links: [
+ loggerLink({
+ enabled: (opts) =>
+ process.env.NODE_ENV === 'development' ||
+ (opts.direction === 'down' && opts.result instanceof Error),
+ }),
+ httpBatchLink({
+ url: `${getBaseUrl()}/api/trpc`,
+ }),
+ ],
+ };
+ },
+ /**
+ * Whether tRPC should await queries when server rendering pages.
+ *
+ * @see https://trpc.io/docs/nextjs#ssr-boolean-default-false
+ */
+ ssr: false,
+});
+
+/**
+ * Inference helper for inputs.
+ *
+ * @example type HelloInput = RouterInputs['example']['hello']
+ */
+export type RouterInputs = inferRouterInputs;
+
+/**
+ * Inference helper for outputs.
+ *
+ * @example type HelloOutput = RouterOutputs['example']['hello']
+ */
+export type RouterOutputs = inferRouterOutputs;
diff --git a/src/utils/numbers.ts b/src/utils/numbers.ts
new file mode 100644
index 0000000..86df9de
--- /dev/null
+++ b/src/utils/numbers.ts
@@ -0,0 +1,14 @@
+export function toFixedNumber(num: number) {
+ return num / 100;
+}
+
+export function toInteger(num: number) {
+ return Math.round(num * 100);
+}
+
+export function toUIString(num: number) {
+ return toFixedNumber(Math.abs(num)).toLocaleString(undefined, {
+ minimumFractionDigits: 2,
+ maximumFractionDigits: 2,
+ });
+}
diff --git a/tailwind.config.ts b/tailwind.config.ts
new file mode 100644
index 0000000..ca7458f
--- /dev/null
+++ b/tailwind.config.ts
@@ -0,0 +1,89 @@
+import type { Config } from 'tailwindcss';
+
+const config = {
+ darkMode: ['class'],
+ content: [
+ './pages/**/*.{ts,tsx}',
+ './components/**/*.{ts,tsx}',
+ './app/**/*.{ts,tsx}',
+ './src/**/*.{ts,tsx}',
+ ],
+ prefix: '',
+ theme: {
+ container: {
+ center: true,
+ padding: '2rem',
+ screens: {
+ '2xl': '1400px',
+ },
+ },
+ extend: {
+ colors: {
+ border: 'hsl(var(--border))',
+ input: 'hsl(var(--input))',
+ ring: 'hsl(var(--ring))',
+ background: 'hsl(var(--background))',
+ foreground: 'hsl(var(--foreground))',
+ primary: {
+ DEFAULT: 'hsl(var(--primary))',
+ foreground: 'hsl(var(--primary-foreground))',
+ },
+ secondary: {
+ DEFAULT: 'hsl(var(--secondary))',
+ foreground: 'hsl(var(--secondary-foreground))',
+ },
+ destructive: {
+ DEFAULT: 'hsl(var(--destructive))',
+ foreground: 'hsl(var(--destructive-foreground))',
+ },
+ muted: {
+ DEFAULT: 'hsl(var(--muted))',
+ foreground: 'hsl(var(--muted-foreground))',
+ },
+ accent: {
+ DEFAULT: 'hsl(var(--accent))',
+ foreground: 'hsl(var(--accent-foreground))',
+ },
+ popover: {
+ DEFAULT: 'hsl(var(--popover))',
+ foreground: 'hsl(var(--popover-foreground))',
+ },
+ card: {
+ DEFAULT: 'hsl(var(--card))',
+ foreground: 'hsl(var(--card-foreground))',
+ },
+ },
+ borderRadius: {
+ lg: 'var(--radius)',
+ md: 'calc(var(--radius) - 2px)',
+ sm: 'calc(var(--radius) - 4px)',
+ },
+ keyframes: {
+ 'accordion-down': {
+ from: { height: '0' },
+ to: { height: 'var(--radix-accordion-content-height)' },
+ },
+ 'accordion-up': {
+ from: { height: 'var(--radix-accordion-content-height)' },
+ to: { height: '0' },
+ },
+ 'caret-blink': {
+ '0%,70%,100%': { opacity: '1' },
+ '20%,50%': { opacity: '0' },
+ },
+ },
+ animation: {
+ 'accordion-down': 'accordion-down 0.2s ease-out',
+ 'accordion-up': 'accordion-up 0.2s ease-out',
+ 'caret-blink': 'caret-blink 1.25s ease-out infinite',
+ },
+ // fontFamily: {
+ // sans: ["var(--font-geist-sans)"],
+ // mono: ["var(--font-geist-mono)"],
+ // },
+ },
+ },
+ plugins: [require('tailwindcss-animate')],
+} satisfies Config;
+
+export default config;
diff --git a/theme.config.jsx b/theme.config.jsx
new file mode 100644
index 0000000..3d0079e
--- /dev/null
+++ b/theme.config.jsx
@@ -0,0 +1,36 @@
+/* eslint-disable @typescript-eslint/ban-ts-comment */
+/* eslint-disable @typescript-eslint/no-unsafe-member-access */
+
+import Link from 'next/link';
+import { Separator } from '~/components/ui/separator';
+
+/* eslint-disable @typescript-eslint/no-unsafe-assignment */
+const config = {
+ footer: (
+
+
+ App
+
+
+
+ Github
+
+
+
+ Product Hunt
+
+
+ ),
+ // @ts-expect-error
+ head: ({ title, meta }) => (
+ <>
+ {meta.description && }
+ {meta.tag && }
+ {meta.author && }
+ >
+ ),
+ readMore: 'Read More →',
+ postFooter: null,
+};
+
+export default config;
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..9865e5d
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,43 @@
+{
+ "compilerOptions": {
+ /* Base Options: */
+ "esModuleInterop": true,
+ "skipLibCheck": true,
+ "target": "es2022",
+ "allowJs": true,
+ "resolveJsonModule": true,
+ "moduleDetection": "force",
+ "isolatedModules": true,
+
+ /* Strictness */
+ "strict": true,
+ "noUncheckedIndexedAccess": true,
+ "checkJs": true,
+
+ /* Bundled projects */
+ "lib": ["dom", "dom.iterable", "ES2022"],
+ "noEmit": true,
+ "module": "ESNext",
+ "moduleResolution": "Bundler",
+ "jsx": "preserve",
+ "plugins": [{ "name": "next" }],
+ "incremental": true,
+
+ /* Path Aliases */
+ "baseUrl": ".",
+ "paths": {
+ "~/*": ["./src/*"],
+ },
+ },
+ "include": [
+ ".eslintrc.cjs",
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ "**/*.jsx",
+ "**/*.cjs",
+ "**/*.js",
+ ".next/types/**/*.ts",
+ ],
+ "exclude": ["node_modules", "public"],
+}
diff --git a/worker/index.ts b/worker/index.ts
new file mode 100644
index 0000000..eded9fc
--- /dev/null
+++ b/worker/index.ts
@@ -0,0 +1,37 @@
+import { type PushMessage } from '~/types';
+
+declare let self: ServiceWorkerGlobalScope;
+
+self.addEventListener('push', function (event) {
+ if (!event) return;
+ const data = JSON.parse(event?.data?.text() ?? '{}') as PushMessage;
+ event.waitUntil(
+ self.registration.showNotification(data.title, {
+ body: data.message,
+ icon: '/icons/android-chrome-192x192.png',
+ }),
+ );
+});
+
+self.addEventListener('notificationclick', function (event) {
+ if (!event) return;
+
+ event.notification.close();
+ event.waitUntil(
+ self.clients
+ .matchAll({ type: 'window', includeUncontrolled: true })
+ .then(function (clientList) {
+ if (clientList.length > 0) {
+ let client = clientList[0];
+ for (const _client of clientList) {
+ if (_client.focused) {
+ client = _client;
+ }
+ }
+
+ return client?.focus();
+ }
+ return self.clients?.openWindow('/');
+ }),
+ );
+});