commit 91a8840c3cec332a12b615a34c927f86ddf42a57 Author: Dries Augustyns Date: Tue Jul 23 13:49:48 2024 +0200 Initial Commit diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..17affd1 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +.env +.yarn/ +.next/ +.github/ +dist/ +assets/ +node_modules/ \ No newline at end of file diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml new file mode 100644 index 0000000..ce0355a --- /dev/null +++ b/.github/workflows/docker-build.yml @@ -0,0 +1,31 @@ +name: Build and Push Docker image + +on: + push: + branches: + - main + +jobs: + docker: + runs-on: ubuntu-latest + steps: + - name: Check out the repo + uses: actions/checkout@v2 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Log in to Docker Hub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Build and push + uses: docker/build-push-action@v2 + with: + context: . + file: ./Dockerfile + push: true + tags: driaug/plunk:latest + platforms: linux/amd64,linux/arm64 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d8d8242 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +node_modules +dist +.idea +.vscode +*.log +.env +.tscache +.next +.out +build +.DS_Store +.yarn/install-state.gz diff --git a/.yarnrc.yml b/.yarnrc.yml new file mode 100644 index 0000000..3186f3f --- /dev/null +++ b/.yarnrc.yml @@ -0,0 +1 @@ +nodeLinker: node-modules diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3dfa50a --- /dev/null +++ b/Dockerfile @@ -0,0 +1,35 @@ +# Base Stage +FROM node:alpine AS base + +WORKDIR /app + +COPY . . + +ARG NEXT_PUBLIC_API_URI=PLUNK_API_URI + +RUN yarn install --network-timeout 1000000 +RUN yarn build:shared +RUN yarn workspace @plunk/api build +RUN yarn workspace @plunk/dashboard build + +# Final Stage +FROM node:alpine + +WORKDIR /app + +RUN apk add --no-cache bash nginx + +COPY --from=base /app/packages/api/dist /app/packages/api/ +COPY --from=base /app/packages/dashboard/.next /app/packages/dashboard/.next +COPY --from=base /app/packages/dashboard/public /app/packages/dashboard/public +COPY --from=base /app/node_modules /app/node_modules +COPY --from=base /app/packages/shared /app/packages/shared +COPY --from=base /app/prisma /app/prisma +COPY deployment/nginx.conf /etc/nginx/nginx.conf +COPY deployment/entry.sh deployment/replace-variables.sh /app/ + +RUN chmod +x /app/entry.sh /app/replace-variables.sh + +EXPOSE 3000 + +CMD ["sh", "/app/entry.sh"] \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..760b4cd --- /dev/null +++ b/README.md @@ -0,0 +1,28 @@ +![card.png](/assets/card.png) + +

Plunk

+ +

+ The Open-Source Email Platform for AWS +

+ +

+ + + +

+ +## Introduction +Plunk is an open-source email platform built on top of AWS SES. It allows you to easily send emails from your applications. +It can be considered as a self-hosted alternative to services like [SendGrid](https://sendgrid.com/), [Resend](https://resend.com) or [Mailgun](https://www.mailgun.com/). + +## Features +- **Transactional Emails**: Send emails straight from your API +- **Automations**: Create automations based on user actions +- **Broadcasts**: Send newsletters and product updates to big audiences + +## Self-hosting Plunk +The easiest way to self-host Plunk is by using the `driaug/plunk` Docker image. +You can pull the latest image from [Docker Hub](https://hub.docker.com/r/driaug/plunk/). + +A complete guide on how to deploy Plunk can be found in the [documentation](https://docs.useplunk.com/getting-started/self-hosting). diff --git a/assets/card.png b/assets/card.png new file mode 100644 index 0000000..7b8f7c9 Binary files /dev/null and b/assets/card.png differ diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..0a914e0 --- /dev/null +++ b/biome.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json", + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "a11y": { + "useKeyWithClickEvents": "off", + "noSvgWithoutTitle": "off", + "useButtonType": "off" + }, + "complexity": { + "noForEach": "off", + "noStaticOnlyClass": "off" + } + } + } +} diff --git a/deployment/entry.sh b/deployment/entry.sh new file mode 100644 index 0000000..3aaeab7 --- /dev/null +++ b/deployment/entry.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +echo "Starting Prisma migrations..." +npx prisma migrate deploy +echo "Prisma migrations completed." + +sh replace-variables.sh && + +nginx & + +echo "Starting the API server..." +node packages/api/app.js & +echo "API server started in the background." + +echo "Starting the Dashboard..." +cd packages/dashboard +npx next start -p 5000 -H 0.0.0.0 +echo "Dashboard started." diff --git a/deployment/nginx.conf b/deployment/nginx.conf new file mode 100644 index 0000000..a2cd7fa --- /dev/null +++ b/deployment/nginx.conf @@ -0,0 +1,25 @@ +events { + worker_connections 1024; +} + +http { + server { + listen 3000; + + location /api/ { + proxy_pass http://plunk:4000/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location / { + proxy_pass http://plunk:5000; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + } +} \ No newline at end of file diff --git a/deployment/replace-variables.sh b/deployment/replace-variables.sh new file mode 100644 index 0000000..85f21a4 --- /dev/null +++ b/deployment/replace-variables.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +echo "Baking Environment Variables..." + +if [ -z "${API_URI}" ]; then + echo "API_URI is not set. Exiting..." + exit 1 +fi + +# Find and replace baked values with real values for the API_URI +find /app/packages/dashboard/public /app/packages/dashboard/.next -type f -name "*.js" | +while read file; do + sed -i "s|PLUNK_API_URI|${API_URI}|g" "$file" +done + +echo "Environment Variables Baked." \ No newline at end of file diff --git a/lerna.json b/lerna.json new file mode 100644 index 0000000..ca08154 --- /dev/null +++ b/lerna.json @@ -0,0 +1,7 @@ +{ + "npmClient": "yarn", + "packages": [ + "packages/*" + ], + "version": "1.0.0" +} \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..eff921e --- /dev/null +++ b/package.json @@ -0,0 +1,38 @@ +{ + "name": "plunk", + "version": "1.0.0", + "private": true, + "license": "MIT", + "workspaces": { + "packages": [ + "packages/*" + ] + }, + "engines": { + "npm": ">=6.14.x", + "yarn": "1.22.x", + "node": ">=18.x" + }, + "devDependencies": { + "@biomejs/biome": "^1.8.3", + "lerna": "^8.1.6", + "prisma": "^5.17.0", + "rimraf": "^5.0.9" + }, + "dependencies": { + "@prisma/client": "^5.17.0" + }, + "scripts": { + "dev:api": "yarn workspace @plunk/api dev", + "dev:dashboard": "yarn workspace @plunk/dashboard dev", + "dev:shared": "yarn workspace @plunk/shared dev", + "build:api": "yarn build:shared && yarn workspace @plunk/api build", + "build:dashboard": "yarn build:shared && yarn workspace @plunk/dashboard build", + "build:shared": "yarn generate && yarn workspace @plunk/shared build", + "clean": "rimraf node_modules yarn.lock && yarn add lerna -DW && lerna run clean", + "preinstall": "node tools/preinstall.js", + "migrate": "prisma migrate dev", + "migrate:deploy": "prisma migrate deploy", + "generate": "prisma generate" + } +} diff --git a/packages/api/.env.example b/packages/api/.env.example new file mode 100644 index 0000000..b7f6bae --- /dev/null +++ b/packages/api/.env.example @@ -0,0 +1,13 @@ +# ENV +JWT_SECRET=mysupersecretJWTsecret +REDIS_URL=redis://127.0.0.1:6379 +DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres +NODE_ENV=development + +# AWS +AWS_REGION= +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_SES_CONFIGURATION_SET= +AWS_CLOUDFRONT_DISTRIBUTION_ID= +AWS_S3_BUCKET= \ No newline at end of file diff --git a/packages/api/package.json b/packages/api/package.json new file mode 100644 index 0000000..abfd452 --- /dev/null +++ b/packages/api/package.json @@ -0,0 +1,51 @@ +{ + "name": "@plunk/api", + "version": "1.0.0", + "main": "dist/index.js", + "private": true, + "scripts": { + "dev": "cross-env NODE_ENV=development ts-node-dev --transpile-only --exit-child src/app.ts", + "start": "node ./dist/app.js", + "build": "tsc", + "clean": "rimraf node_modules dist .turbo" + }, + "devDependencies": { + "@types/bcrypt": "^5.0.2", + "@types/compression": "^1.7.5", + "@types/cookie-parser": "^1.4.7", + "@types/cors": "^2.8.17", + "@types/express": "^4.17.21", + "@types/ioredis": "^5.0.0", + "@types/jsonwebtoken": "^9.0.6", + "@types/mjml": "^4.7.4", + "@types/morgan": "^1.9.9", + "@types/node-cron": "^3.0.11", + "@types/signale": "^1.4.7", + "cross-env": "^7.0.3", + "prisma": "^5.17.0", + "ts-node-dev": "^2.0.0", + "typescript": "^5.5.3" + }, + "dependencies": { + "@aws-sdk/client-cloudfront": "^3.616.0", + "@aws-sdk/client-ses": "^3.616.0", + "@overnightjs/core": "^1.7.6", + "@plunk/shared": "^1.0.0", + "@prisma/client": "^5.17.0", + "bcrypt": "^5.1.1", + "body-parser": "^1.20.2", + "compression": "^1.7.4", + "cookie-parser": "^1.4.6", + "cors": "^2.8.5", + "dotenv": "^16.4.5", + "express": "^4.19.2", + "express-async-errors": "^3.1.1", + "helmet": "^7.1.0", + "ioredis": "^5.4.1", + "jsonwebtoken": "^9.0.2", + "mjml": "^4.15.3", + "morgan": "^1.10.0", + "node-cron": "^3.0.3", + "signale": "^1.4.0" + } +} diff --git a/packages/api/src/app.ts b/packages/api/src/app.ts new file mode 100644 index 0000000..037f220 --- /dev/null +++ b/packages/api/src/app.ts @@ -0,0 +1,108 @@ +import "dotenv/config"; +import "express-async-errors"; + +import { STATUS_CODES } from "node:http"; +import { Server } from "@overnightjs/core"; +import compression from "compression"; +import cookies from "cookie-parser"; +import cors from "cors"; +import { type NextFunction, type Request, type Response, json } from "express"; +import helmet from "helmet"; +import morgan from "morgan"; +import signale from "signale"; +import { API_URI, NODE_ENV, PORT } from "./app/constants"; +import { task } from "./app/cron"; +import { Auth } from "./controllers/Auth"; +import { Identities } from "./controllers/Identities"; +import { Memberships } from "./controllers/Memberships"; +import { Projects } from "./controllers/Projects"; +import { Tasks } from "./controllers/Tasks"; +import { Users } from "./controllers/Users"; +import { Webhooks } from "./controllers/Webhooks"; +import { V1 } from "./controllers/v1"; +import { prisma } from "./database/prisma"; +import { HttpException } from "./exceptions"; + +const server = new (class extends Server { + public constructor() { + super(); + + // Set the content-type to JSON for any request coming from AWS SNS + this.app.use((req, res, next) => { + if (req.get("x-amz-sns-message-type")) { + req.headers["content-type"] = "application/json"; + } + next(); + }); + + this.app.use( + compression({ + threshold: 0, + }), + ); + + // Parse the rest of our application as json + this.app.use(json({ limit: "50mb" })); + this.app.use(cookies()); + this.app.use(helmet()); + + this.app.use(["/v1", "/v1/track", "/v1/send"], (req, res, next) => { + res.set({ "Access-Control-Allow-Origin": "*" }); + next(); + }); + + this.app.use( + cors({ + origin: [API_URI], + credentials: true, + }), + ); + + this.app.use(morgan(NODE_ENV === "development" ? "dev" : "short")); + + this.addControllers([ + new Auth(), + new Users(), + new Projects(), + new Memberships(), + new Webhooks(), + new Identities(), + new Tasks(), + new V1(), + ]); + + this.app.use("*", () => { + throw new HttpException(404, "Unknown route"); + }); + } +})(); + +server.app.use((req, res, next) => { + console.log(`Incoming request: ${req.method} ${req.path}`); + next(); +}); + +server.app.use( + (error: Error, req: Request, res: Response, _next: NextFunction) => { + const code = error instanceof HttpException ? error.code : 500; + + if (NODE_ENV !== "development") { + signale.error(error); + } + + res.status(code).json({ + code, + error: STATUS_CODES[code], + message: error.message, + time: Date.now(), + }); + }, +); + +void prisma.$connect().then(() => { + server.app.listen(PORT, () => { + task.start(); + + signale.success("[HTTPS] Ready on", PORT); + }); +}); diff --git a/packages/api/src/app/constants.ts b/packages/api/src/app/constants.ts new file mode 100644 index 0000000..e79fc64 --- /dev/null +++ b/packages/api/src/app/constants.ts @@ -0,0 +1,42 @@ +/** + * Safely parse environment variables + * @param key The key + * @param defaultValue An optional default value if the environment variable does not exist + */ +export function validateEnv( + key: keyof NodeJS.ProcessEnv, + defaultValue?: T, +): T { + const value = process.env[key] as T | undefined; + + if (!value) { + if (typeof defaultValue !== "undefined") { + return defaultValue; + } + throw new Error(`${key} is not defined in environment variables`); + } + + return value; +} + +// ENV +export const JWT_SECRET = validateEnv("JWT_SECRET"); +export const PORT = validateEnv<`${number}`>("PORT", "4000"); +export const NODE_ENV = validateEnv<"development" | "production">( + "NODE_ENV", + "production", +); + +export const REDIS_URL = validateEnv("REDIS_URL"); + +// URLs +export const API_URI = validateEnv("API_URI", "http://localhost:8080"); +export const APP_URI = validateEnv("APP_URI", "http://localhost:3000"); + +// AWS +export const AWS_REGION = validateEnv("AWS_REGION"); +export const AWS_ACCESS_KEY_ID = validateEnv("AWS_SES_ACCESS_KEY_ID"); +export const AWS_SECRET_ACCESS_KEY = validateEnv("AWS_SES_SECRET_ACCESS_KEY"); +export const AWS_SES_CONFIGURATION_SET = validateEnv( + "AWS_SES_CONFIGURATION_SET", +); diff --git a/packages/api/src/app/cron.ts b/packages/api/src/app/cron.ts new file mode 100644 index 0000000..9ba73a5 --- /dev/null +++ b/packages/api/src/app/cron.ts @@ -0,0 +1,15 @@ +import cron from 'node-cron'; +import {API_URI} from './constants'; +import signale from 'signale'; + +export const task = cron.schedule('* * * * *', () => { + signale.info('Running scheduled tasks'); + void fetch(`${API_URI}/tasks`, { + method: 'POST', + }); + + signale.info('Updating verified identities'); + void fetch(`${API_URI}/identities/update`, { + method: 'POST', + }); +}); diff --git a/packages/api/src/controllers/Auth.ts b/packages/api/src/controllers/Auth.ts new file mode 100644 index 0000000..a7048c2 --- /dev/null +++ b/packages/api/src/controllers/Auth.ts @@ -0,0 +1,125 @@ +import { Controller, Get, Post } from "@overnightjs/core"; +import { UserSchemas, UtilitySchemas } from "@plunk/shared"; +import type { Request, Response } from "express"; +import { prisma } from "../database/prisma"; +import { NotAllowed, NotFound } from "../exceptions"; +import { jwt } from "../middleware/auth"; +import { AuthService } from "../services/AuthService"; +import { UserService } from "../services/UserService"; +import { Keys } from "../services/keys"; +import { REDIS_ONE_MINUTE, redis } from "../services/redis"; +import { createHash } from "../util/hash"; + +@Controller("auth") +export class Auth { + @Post("login") + public async login(req: Request, res: Response) { + const { email, password } = UserSchemas.credentials.parse(req.body); + + const user = await UserService.email(email); + + if (!user) { + return res.json({ success: false, data: "Incorrect email or password" }); + } + + if (!user.password) { + return res.json({ + success: "redirect", + redirect: `/auth/reset?id=${user.id}`, + }); + } + + const verified = await AuthService.verifyCredentials(email, password); + + if (!verified) { + return res.json({ success: false, data: "Incorrect email or password" }); + } + + await redis.set( + Keys.User.id(user.id), + JSON.stringify(user), + "EX", + REDIS_ONE_MINUTE * 60, + ); + + const token = jwt.sign(user.id); + const cookie = UserService.cookieOptions(); + + return res + .cookie(UserService.COOKIE_NAME, token, cookie) + .json({ success: true, data: { id: user.id, email: user.email } }); + } + + @Post("signup") + public async signup(req: Request, res: Response) { + const { email, password } = UserSchemas.credentials.parse(req.body); + + const user = await UserService.email(email); + + if (user) { + return res.json({ + success: false, + data: "That email is already associated with another user", + }); + } + + const created_user = await prisma.user.create({ + data: { + email, + password: await createHash(password), + }, + }); + + await redis.set( + Keys.User.id(created_user.id), + JSON.stringify(created_user), + "EX", + REDIS_ONE_MINUTE * 60, + ); + + const token = jwt.sign(created_user.id); + const cookie = UserService.cookieOptions(); + + return res.cookie(UserService.COOKIE_NAME, token, cookie).json({ + success: true, + data: { id: created_user.id, email: created_user.email }, + }); + } + + @Post("reset") + public async reset(req: Request, res: Response) { + const { id, password } = UtilitySchemas.id + .merge(UserSchemas.credentials.pick({ password: true })) + .parse(req.body); + + const user = await UserService.id(id); + + if (!user) { + throw new NotFound("user"); + } + + if (user.password) { + throw new NotAllowed(); + } + + await prisma.user.update({ + where: { id }, + data: { password: await createHash(password) }, + }); + + await redis.del(Keys.User.id(user.id)); + await redis.del(Keys.User.email(user.email)); + + return res.json({ success: true }); + } + + @Get("logout") + public logout(req: Request, res: Response) { + res.cookie( + UserService.COOKIE_NAME, + "", + UserService.cookieOptions(new Date()), + ); + return res.json(true); + } +} diff --git a/packages/api/src/controllers/Identities.ts b/packages/api/src/controllers/Identities.ts new file mode 100644 index 0000000..c5c64a6 --- /dev/null +++ b/packages/api/src/controllers/Identities.ts @@ -0,0 +1,171 @@ +import { Controller, Get, Middleware, Post } from "@overnightjs/core"; +import { IdentitySchemas, UtilitySchemas } from "@plunk/shared"; +import type { Request, Response } from "express"; +import signale from "signale"; +import { prisma } from "../database/prisma"; +import { NotFound } from "../exceptions"; +import { type IJwt, isAuthenticated } from "../middleware/auth"; +import { ProjectService } from "../services/ProjectService"; +import { Keys } from "../services/keys"; +import { redis } from "../services/redis"; +import { + getIdentities, + getIdentityVerificationAttributes, + ses, + verifyIdentity, +} from "../util/ses"; + +@Controller("identities") +export class Identities { + @Get("id/:id") + @Middleware([isAuthenticated]) + public async getVerification(req: Request, res: Response) { + const { id } = UtilitySchemas.id.parse(req.params); + + const project = await ProjectService.id(id); + + if (!project) { + throw new NotFound("project"); + } + + if (!project.email) { + return res.status(200).json({ success: false }); + } + + const attributes = await getIdentityVerificationAttributes(project.email); + + if (attributes.status === "Success" && !project.verified) { + await prisma.project.update({ where: { id }, data: { verified: true } }); + + await redis.del(Keys.Project.id(project.id)); + await redis.del(Keys.Project.secret(project.secret)); + await redis.del(Keys.Project.public(project.public)); + } + + return res.status(200).json({ tokens: attributes.tokens }); + } + + @Middleware([isAuthenticated]) + @Post("create") + public async addIdentity(req: Request, res: Response) { + const { id, email } = IdentitySchemas.create.parse(req.body); + + const { userId } = res.locals.auth as IJwt; + + const project = await ProjectService.id(id); + + if (!project) { + throw new NotFound("project"); + } + + const existingProject = await prisma.project.findFirst({ + where: { email: { endsWith: email.split("@")[1] } }, + }); + + if (existingProject) { + throw new Error("Domain already attached to another project"); + } + + const tokens = await verifyIdentity(email); + + await prisma.project.update({ + where: { id }, + data: { email, verified: false }, + }); + + await redis.del(Keys.User.projects(userId)); + await redis.del(Keys.Project.id(project.id)); + + return res.status(200).json({ success: true, tokens }); + } + + @Middleware([isAuthenticated]) + @Post("reset") + public async resetIdentity(req: Request, res: Response) { + const { id } = UtilitySchemas.id.parse(req.body); + + const { userId } = res.locals.auth as IJwt; + + const project = await ProjectService.id(id); + + if (!project) { + throw new NotFound("project"); + } + + await prisma.project.update({ + where: { id }, + data: { email: null, verified: false }, + }); + + await redis.del(Keys.User.projects(userId)); + await redis.del(Keys.Project.id(project.id)); + + return res.status(200).json({ success: true }); + } + + @Post("update") + public async updateIdentities(req: Request, res: Response) { + const count = await prisma.project.count({ + where: { email: { not: null } }, + }); + + for (let i = 0; i < count; i += 99) { + const dbIdentities = await prisma.project.findMany({ + where: { email: { not: null } }, + select: { id: true, email: true }, + skip: i, + take: 99, + }); + + const awsIdentities = await getIdentities( + dbIdentities.map((i) => i.email as string), + ); + + for (const identity of awsIdentities) { + const projectId = dbIdentities.find((i) => + i.email?.endsWith(identity.email), + ); + + const project = await ProjectService.id(projectId?.id as string); + + if (identity.status === "Failed") { + signale.info(`Restarting verification for ${identity.email}`); + try { + void verifyIdentity(identity.email); + } catch (e) { + // @ts-ignore + if (e.Code === "Throttling") { + signale.warn("Throttling detected, waiting 5 seconds"); + await new Promise((r) => setTimeout(r, 5000)); + } + } + } + + await prisma.project.update({ + where: { id: projectId?.id as string }, + data: { verified: identity.status === "Success" }, + }); + + if (project && !project.verified && identity.status === "Success") { + signale.success(`Successfully verified ${identity.email}`); + void ses.setIdentityFeedbackForwardingEnabled({ + Identity: identity.email, + ForwardingEnabled: false, + }); + + await redis.del(Keys.Project.id(project.id)); + await redis.del(Keys.Project.secret(project.secret)); + await redis.del(Keys.Project.public(project.public)); + } + + if (project?.verified && identity.status !== "Success") { + await redis.del(Keys.Project.id(project.id)); + await redis.del(Keys.Project.secret(project.secret)); + await redis.del(Keys.Project.public(project.public)); + } + } + } + + return res.status(200).json({ success: true }); + } +} diff --git a/packages/api/src/controllers/Memberships.ts b/packages/api/src/controllers/Memberships.ts new file mode 100644 index 0000000..70bf575 --- /dev/null +++ b/packages/api/src/controllers/Memberships.ts @@ -0,0 +1,147 @@ +import { Controller, Middleware, Post } from "@overnightjs/core"; +import { MembershipSchemas, UtilitySchemas } from "@plunk/shared"; +import type { Request, Response } from "express"; +import { + HttpException, + NotAllowed, + NotAuthenticated, + NotFound, +} from "../exceptions"; +import { type IJwt, isAuthenticated } from "../middleware/auth"; +import { MembershipService } from "../services/MembershipService"; +import { ProjectService } from "../services/ProjectService"; +import { UserService } from "../services/UserService"; +import { Keys } from "../services/keys"; +import { redis } from "../services/redis"; + +@Controller("memberships") +export class Memberships { + @Middleware([isAuthenticated]) + @Post("invite") + public async inviteMember(req: Request, res: Response) { + const { id: projectId, email } = MembershipSchemas.invite.parse(req.body); + + const { userId } = res.locals.auth as IJwt; + + const project = await ProjectService.id(projectId); + + if (!project) { + throw new NotFound("project"); + } + + const isAdmin = await MembershipService.isAdmin(projectId, userId); + + if (!isAdmin) { + throw new NotAllowed(); + } + + const invitedUser = await UserService.email(email); + + if (!invitedUser) { + throw new HttpException( + 404, + "We could not find that user, please ask them to sign up first.", + ); + } + + const alreadyMember = await MembershipService.isMember( + project.id, + invitedUser.id, + ); + + if (alreadyMember) { + throw new NotAllowed(); + } + + await MembershipService.invite(projectId, invitedUser.id, "ADMIN"); + + const memberships = await ProjectService.memberships(projectId); + + return res.status(200).json({ success: true, memberships }); + } + + @Middleware([isAuthenticated]) + @Post("kick") + public async kickMember(req: Request, res: Response) { + const { id: projectId, email } = MembershipSchemas.kick.parse(req.body); + + const { userId } = res.locals.auth as IJwt; + + const user = await UserService.id(userId); + + if (!user) { + throw new NotAuthenticated(); + } + + const project = await ProjectService.id(projectId); + + if (!project) { + throw new NotFound("project"); + } + + const isAdmin = await MembershipService.isAdmin(projectId, userId); + + if (!isAdmin) { + throw new NotAllowed(); + } + + const kickedUser = await UserService.email(email); + + if (!kickedUser) { + throw new NotFound("user"); + } + + const isMember = await MembershipService.isMember( + project.id, + kickedUser.id, + ); + + if (!isMember) { + throw new NotAllowed(); + } + + if (userId === kickedUser.id) { + throw new NotAllowed(); + } + + await MembershipService.kick(projectId, kickedUser.id); + + const memberships = await ProjectService.memberships(projectId); + + return res.status(200).json({ success: true, memberships }); + } + + @Middleware([isAuthenticated]) + @Post("leave") + public async leaveProject(req: Request, res: Response) { + const { id: projectId } = UtilitySchemas.id.parse(req.body); + + const { userId } = res.locals.auth as IJwt; + + const user = await UserService.id(userId); + + if (!user) { + throw new NotAuthenticated(); + } + + const project = await ProjectService.id(projectId); + + if (!project) { + throw new NotFound("project"); + } + + const isMember = await MembershipService.isMember(projectId, userId); + + if (!isMember) { + throw new NotAllowed(); + } + + await MembershipService.kick(projectId, userId); + + await redis.del(Keys.User.projects(userId)); + + const memberships = await UserService.projects(userId); + + return res.status(200).json({ success: true, memberships }); + } +} diff --git a/packages/api/src/controllers/Projects.ts b/packages/api/src/controllers/Projects.ts new file mode 100644 index 0000000..d483703 --- /dev/null +++ b/packages/api/src/controllers/Projects.ts @@ -0,0 +1,624 @@ +import { + Controller, + Delete, + Get, + Middleware, + Post, + Put, +} from "@overnightjs/core"; +import { IdentitySchemas, ProjectSchemas, UtilitySchemas } from "@plunk/shared"; +import type { Request, Response } from "express"; +import z from "zod"; +import { prisma } from "../database/prisma"; +import { NotAllowed, NotAuthenticated, NotFound } from "../exceptions"; +import { type IJwt, isAuthenticated } from "../middleware/auth"; +import { MembershipService } from "../services/MembershipService"; +import { ProjectService } from "../services/ProjectService"; +import { UserService } from "../services/UserService"; +import { Keys } from "../services/keys"; +import { redis } from "../services/redis"; +import { generateToken } from "../util/tokens"; + +@Controller("projects") +export class Projects { + @Get("id/:id") + @Middleware([isAuthenticated]) + public async getProjectByID(req: Request, res: Response) { + const { id: projectId } = UtilitySchemas.id.parse(req.params); + + const { userId } = res.locals.auth as IJwt; + + const project = await ProjectService.id(projectId); + + if (!project) { + throw new NotFound("project"); + } + + const isMember = await MembershipService.isMember(projectId, userId); + + if (!isMember) { + throw new NotAllowed(); + } + + return res.json(project); + } + + @Get("id/:id/memberships") + @Middleware([isAuthenticated]) + public async getProjectMembershipsByID(req: Request, res: Response) { + const { id: projectId } = UtilitySchemas.id.parse(req.params); + + const { userId } = res.locals.auth as IJwt; + + const project = await ProjectService.id(projectId); + + if (!project) { + throw new NotFound("project"); + } + + const isMember = await MembershipService.isMember(projectId, userId); + + if (!isMember) { + throw new NotAllowed(); + } + + const memberships = await ProjectService.memberships(projectId); + + return res.status(200).json(memberships); + } + + @Get("id/:id/usage") + @Middleware([isAuthenticated]) + public async getProjectUsageByID(req: Request, res: Response) { + const { id: projectId } = UtilitySchemas.id.parse(req.params); + + const { userId } = res.locals.auth as IJwt; + + const project = await ProjectService.id(projectId); + + if (!project) { + throw new NotFound("project"); + } + + const isMember = await MembershipService.isMember(projectId, userId); + + if (!isMember) { + throw new NotAllowed(); + } + + const usage = await ProjectService.usage(projectId); + + return res.status(200).json(usage); + } + + @Get("id/:id/events") + @Middleware([isAuthenticated]) + public async getProjectEventsByID(req: Request, res: Response) { + const { id: projectId } = UtilitySchemas.id.parse(req.params); + + const { triggers } = z + .object({ + triggers: z + .boolean() + .optional() + .default(true) + .or(z.string().transform((str) => str.toLowerCase() === "true")), + }) + .parse(req.query); + + const { userId } = res.locals.auth as IJwt; + + const project = await ProjectService.id(projectId); + + if (!project) { + throw new NotFound("project"); + } + + const isMember = await MembershipService.isMember(projectId, userId); + + if (!isMember) { + throw new NotAllowed(); + } + + const events = await ProjectService.events(projectId, triggers); + + return res.status(200).json(events); + } + + @Get("id/:id/actions") + @Middleware([isAuthenticated]) + public async getProjectActionsByID(req: Request, res: Response) { + const { id: projectId } = UtilitySchemas.id.parse(req.params); + + const { userId } = res.locals.auth as IJwt; + + const project = await ProjectService.id(projectId); + + if (!project) { + throw new NotFound("project"); + } + + const isMember = await MembershipService.isMember(projectId, userId); + + if (!isMember) { + throw new NotAllowed(); + } + + const actions = await ProjectService.actions(projectId); + + return res.status(200).json(actions); + } + + @Get("id/:id/templates") + @Middleware([isAuthenticated]) + public async getProjectTemplatesByID(req: Request, res: Response) { + const { id: projectId } = UtilitySchemas.id.parse(req.params); + + const { userId } = res.locals.auth as IJwt; + + const project = await ProjectService.id(projectId); + + if (!project) { + throw new NotFound("project"); + } + + const isMember = await MembershipService.isMember(projectId, userId); + + if (!isMember) { + throw new NotAllowed(); + } + + const templates = await ProjectService.templates(projectId); + + return res.status(200).json(templates); + } + + @Get("id/:id/contacts/search") + @Middleware([isAuthenticated]) + public async searchContacts(req: Request, res: Response) { + const { id: projectId } = UtilitySchemas.id.parse(req.params); + + const { userId } = res.locals.auth as IJwt; + + const project = await ProjectService.id(projectId); + + if (!project) { + throw new NotFound("project"); + } + + const isMember = await MembershipService.isMember(projectId, userId); + + if (!isMember) { + throw new NotAllowed(); + } + + const { query } = z.object({ query: z.string().min(1) }).parse(req.query); + + const contacts = await prisma.contact.findMany({ + where: { + projectId: project.id, + OR: [ + { email: { contains: query, mode: "insensitive" } }, + { data: { contains: query, mode: "insensitive" } }, + ], + }, + select: { + id: true, + email: true, + subscribed: true, + createdAt: true, + triggers: { select: { createdAt: true } }, + emails: { select: { createdAt: true } }, + }, + orderBy: [{ createdAt: "desc" }], + }); + + return res.status(200).json({ + contacts, + count: contacts.length, + }); + } + + @Get("id/:id/contacts/count") + @Middleware([isAuthenticated]) + public async getProjectContactCountByID(req: Request, res: Response) { + const { id: projectId } = UtilitySchemas.id.parse(req.params); + + const { userId } = res.locals.auth as IJwt; + + const project = await ProjectService.id(projectId); + + if (!project) { + throw new NotFound("project"); + } + + const isMember = await MembershipService.isMember(projectId, userId); + + if (!isMember) { + throw new NotAllowed(); + } + + const count = await ProjectService.contacts.count(projectId); + + return res.status(200).json(count); + } + + @Get("id/:id/contacts/metadata") + @Middleware([isAuthenticated]) + public async getProjectMetadataByID(req: Request, res: Response) { + const { id: projectId } = UtilitySchemas.id.parse(req.params); + + const { userId } = res.locals.auth as IJwt; + + const project = await ProjectService.id(projectId); + + if (!project) { + throw new NotFound("project"); + } + + const isMember = await MembershipService.isMember(projectId, userId); + + if (!isMember) { + throw new NotAllowed(); + } + + const metadata = await ProjectService.metadata(projectId); + + return res.status(200).json(metadata); + } + + @Get("id/:id/contacts") + @Middleware([isAuthenticated]) + public async getProjectContactsByID(req: Request, res: Response) { + const { id: projectId } = UtilitySchemas.id.parse(req.params); + const { page } = UtilitySchemas.pagination.parse(req.query); + + const { userId } = res.locals.auth as IJwt; + + const project = await ProjectService.id(projectId); + + if (!project) { + throw new NotFound("project"); + } + + const isMember = await MembershipService.isMember(projectId, userId); + + if (!isMember) { + throw new NotAllowed(); + } + + if (page === 0) { + const contacts = await ProjectService.contacts.get(projectId); + + return res.status(200).json({ contacts, count: contacts?.length }); + } + const contacts = await ProjectService.contacts.paginated(projectId, page); + const count = await ProjectService.contacts.count(projectId); + + return res.status(200).json({ contacts, count }); + } + + @Get("id/:id/feed") + @Middleware([isAuthenticated]) + public async getProjectFeedByID(req: Request, res: Response) { + const { id: projectId } = UtilitySchemas.id.parse(req.params); + const { page } = UtilitySchemas.pagination.parse(req.query); + + const { userId } = res.locals.auth as IJwt; + + const project = await ProjectService.id(projectId); + + if (!project) { + throw new NotFound("project"); + } + + const isMember = await MembershipService.isMember(projectId, userId); + + if (!isMember) { + throw new NotAllowed(); + } + + const feed = await ProjectService.feed(projectId, page); + + return res.status(200).json(feed); + } + + @Get("id/:id/campaigns") + @Middleware([isAuthenticated]) + public async getProjectCampaignsByID(req: Request, res: Response) { + const { id: projectId } = UtilitySchemas.id.parse(req.params); + + const { userId } = res.locals.auth as IJwt; + + const project = await ProjectService.id(projectId); + + if (!project) { + throw new NotFound("project"); + } + + const isMember = await MembershipService.isMember(projectId, userId); + + if (!isMember) { + throw new NotAllowed(); + } + + const campaigns = await ProjectService.campaigns(projectId); + + return res.status(200).json(campaigns); + } + + @Get("id/:id/emails/count") + @Middleware([isAuthenticated]) + public async getProjectEmailCountByID(req: Request, res: Response) { + const { id: projectId } = UtilitySchemas.id.parse(req.params); + + const { userId } = res.locals.auth as IJwt; + + const project = await ProjectService.id(projectId); + + if (!project) { + throw new NotFound("project"); + } + + const isMember = await MembershipService.isMember(projectId, userId); + + if (!isMember) { + throw new NotAllowed(); + } + + const count = await ProjectService.emails.count(projectId); + + return res.status(200).json(count); + } + + @Get("id/:id/emails") + @Middleware([isAuthenticated]) + public async getProjectEmailsByID(req: Request, res: Response) { + const { id: projectId } = UtilitySchemas.id.parse(req.params); + + const { userId } = res.locals.auth as IJwt; + + const project = await ProjectService.id(projectId); + + if (!project) { + throw new NotFound("project"); + } + + const isMember = await MembershipService.isMember(projectId, userId); + + if (!isMember) { + throw new NotAllowed(); + } + + const emails = await ProjectService.emails.get(projectId); + + return res.status(200).json(emails); + } + + @Get("id/:id/analytics") + @Middleware([isAuthenticated]) + public async getProjectAnalyticsByID(req: Request, res: Response) { + const { id: projectId } = UtilitySchemas.id.parse(req.params); + const { method } = ProjectSchemas.analytics.parse(req.query); + + const { userId } = res.locals.auth as IJwt; + + const project = await ProjectService.id(projectId); + + if (!project) { + throw new NotFound("project"); + } + + const isMember = await MembershipService.isMember(projectId, userId); + + if (!isMember) { + throw new NotAllowed(); + } + + const analytics = await ProjectService.analytics({ id: projectId, method }); + + return res.status(200).json(analytics); + } + + @Post("create") + @Middleware([isAuthenticated]) + public async createProject(req: Request, res: Response) { + const { name, url } = ProjectSchemas.create.parse(req.body); + + const { userId } = res.locals.auth as IJwt; + + const user = await UserService.id(userId); + + if (!user) { + throw new NotAuthenticated(); + } + + let secretKey = ""; + let secretIsAvailable = false; + + let publicKey = ""; + let publicIsAvailable = false; + + while (!secretIsAvailable) { + secretKey = generateToken("secret"); + + secretIsAvailable = await ProjectService.secretIsAvailable(secretKey); + } + + while (!publicIsAvailable) { + publicKey = generateToken("public"); + + publicIsAvailable = await ProjectService.publicIsAvailable(publicKey); + } + + const project = await prisma.project.create({ + data: { + name, + url, + secret: secretKey, + public: publicKey, + memberships: { + create: [{ userId, role: "OWNER" }], + }, + }, + }); + + await redis.del(Keys.User.projects(userId)); + await redis.del(Keys.Project.secret(project.secret)); + await redis.del(Keys.Project.public(project.public)); + await redis.del(Keys.Project.id(project.id)); + + return res.status(200).json({ success: true, data: project }); + } + + @Post("id/:id/regenerate") + @Middleware([isAuthenticated]) + public async regenerateAPIkey(req: Request, res: Response) { + const { userId } = res.locals.auth as IJwt; + + let project = await ProjectService.id(req.params.id); + + if (!project) { + throw new NotFound("project"); + } + + const user = await UserService.id(userId); + + if (!user) { + throw new NotAuthenticated(); + } + + const isAdmin = await MembershipService.isAdmin(project.id, userId); + + if (!isAdmin) { + throw new NotAllowed(); + } + + let secretKey = ""; + let secretIsAvailable = false; + + let publicKey = ""; + let publicIsAvailable = false; + + while (!secretIsAvailable) { + secretKey = generateToken("secret"); + + secretIsAvailable = await ProjectService.secretIsAvailable(secretKey); + } + + while (!publicIsAvailable) { + publicKey = generateToken("public"); + + publicIsAvailable = await ProjectService.secretIsAvailable(publicKey); + } + + project = await prisma.project.update({ + where: { id: project.id }, + data: { secret: secretKey, public: publicKey }, + }); + + await redis.del(Keys.User.projects(userId)); + + return res.status(200).json({ success: true, project }); + } + + @Put("update") + @Middleware([isAuthenticated]) + public async updateProject(req: Request, res: Response) { + const { id: projectId, name, url } = ProjectSchemas.update.parse(req.body); + + const { userId } = res.locals.auth as IJwt; + + let project = await ProjectService.id(projectId); + + if (!project) { + throw new NotFound("project"); + } + + const isAdmin = await MembershipService.isAdmin(projectId, userId); + + if (!isAdmin) { + throw new NotAllowed(); + } + + project = await prisma.project.update({ + where: { id: projectId }, + data: { + name, + url, + }, + }); + + await redis.del(Keys.Project.id(project.id)); + await redis.del(Keys.Project.secret(project.secret)); + await redis.del(Keys.User.projects(userId)); + + return res.status(200).json({ success: true, data: project }); + } + + @Put("update/identity") + @Middleware([isAuthenticated]) + public async updateIdentity(req: Request, res: Response) { + const { id: projectId, from } = IdentitySchemas.update.parse(req.body); + + const { userId } = res.locals.auth as IJwt; + + let project = await ProjectService.id(projectId); + + if (!project) { + throw new NotFound("project"); + } + + const isAdmin = await MembershipService.isAdmin(projectId, userId); + + if (!isAdmin) { + throw new NotAllowed(); + } + + project = await prisma.project.update({ + where: { id: projectId }, + data: { + from, + }, + }); + + await redis.del(Keys.Project.id(project.id)); + await redis.del(Keys.Project.secret(project.secret)); + await redis.del(Keys.User.projects(userId)); + + return res.status(200).json({ success: true, data: project }); + } + + @Delete("delete") + @Middleware([isAuthenticated]) + public async deleteProject(req: Request, res: Response) { + const { id: projectId } = UtilitySchemas.id.parse(req.body); + + const { userId } = res.locals.auth as IJwt; + + const project = await ProjectService.id(projectId); + + if (!project) { + throw new NotFound("project"); + } + + const user = await UserService.id(userId); + + if (!user) { + throw new NotAuthenticated(); + } + + const isOwner = await MembershipService.isOwner(projectId, userId); + + if (!isOwner) { + throw new NotAllowed(); + } + + await prisma.project.delete({ where: { id: project.id } }); + + await redis.del(Keys.User.projects(userId)); + await redis.del(Keys.Project.id(projectId)); + + return res.status(200).json({ success: true, data: project }); + } +} diff --git a/packages/api/src/controllers/Tasks.ts b/packages/api/src/controllers/Tasks.ts new file mode 100644 index 0000000..b6493be --- /dev/null +++ b/packages/api/src/controllers/Tasks.ts @@ -0,0 +1,140 @@ +import { Controller, Post } from "@overnightjs/core"; +import type { Request, Response } from "express"; +import signale from "signale"; +import { prisma } from "../database/prisma"; +import { ContactService } from "../services/ContactService"; +import { EmailService } from "../services/EmailService"; +import { ProjectService } from "../services/ProjectService"; + +@Controller("tasks") +export class Tasks { + @Post() + public async handleTasks(req: Request, res: Response) { + // Get all tasks with a runBy data in the past + const tasks = await prisma.task.findMany({ + where: { runBy: { lte: new Date() } }, + orderBy: { runBy: "asc" }, + include: { + action: { include: { template: true, notevents: true } }, + campaign: true, + contact: true, + }, + }); + + for (const task of tasks) { + const { action, campaign, contact } = task; + + const project = await ProjectService.id(contact.projectId); + + // If the project does not exist or is disabled, delete all tasks + if (!project) { + await prisma.task.deleteMany({ + where: { + contact: { + projectId: contact.projectId, + }, + }, + }); + continue; + } + + let subject = ""; + let body = ""; + + if (action) { + const { template, notevents } = action; + + if (notevents.length > 0) { + const triggers = await ContactService.triggers(contact.id); + if ( + notevents.some((e) => + triggers.some( + (t) => t.contactId === contact.id && t.eventId === e.id, + ), + ) + ) { + await prisma.task.delete({ where: { id: task.id } }); + continue; + } + } + + ({ subject, body } = EmailService.format({ + subject: template.subject, + body: template.body, + data: { + plunk_id: contact.id, + plunk_email: contact.email, + ...JSON.parse(contact.data ?? "{}"), + }, + })); + } else if (campaign) { + ({ subject, body } = EmailService.format({ + subject: campaign.subject, + body: campaign.body, + data: { + plunk_id: contact.id, + plunk_email: contact.email, + ...JSON.parse(contact.data ?? "{}"), + }, + })); + } + + const { messageId } = await EmailService.send({ + from: { + name: project.from ?? project.name, + email: + project.verified && project.email + ? project.email + : "no-reply@useplunk.dev", + }, + to: [contact.email], + content: { + subject, + html: EmailService.compile({ + content: body, + footer: { + unsubscribe: campaign + ? true + : !!action && action.template.type === "MARKETING", + }, + contact: { + id: contact.id, + }, + project: { + name: project.name, + }, + isHtml: + (campaign && campaign.style === "HTML") ?? + (!!action && action.template.style === "HTML"), + }), + }, + }); + + const emailData: { + messageId: string; + contactId: string; + actionId?: string; + campaignId?: string; + } = { + messageId, + contactId: contact.id, + }; + + if (action) { + emailData.actionId = action.id; + } else if (campaign) { + emailData.campaignId = campaign.id; + } + + await prisma.email.create({ data: emailData }); + + await prisma.task.delete({ where: { id: task.id } }); + + signale.success( + `Task completed for ${contact.email} from ${project.name}`, + ); + } + + return res.status(200).json({ success: true }); + } +} diff --git a/packages/api/src/controllers/Users.ts b/packages/api/src/controllers/Users.ts new file mode 100644 index 0000000..00398dc --- /dev/null +++ b/packages/api/src/controllers/Users.ts @@ -0,0 +1,32 @@ +import { Controller, Get, Middleware } from "@overnightjs/core"; +import type { Request, Response } from "express"; +import { NotAuthenticated } from "../exceptions"; +import { type IJwt, isAuthenticated } from "../middleware/auth"; +import { UserService } from "../services/UserService"; + +@Controller("users") +export class Users { + @Get("@me") + @Middleware([isAuthenticated]) + public async me(req: Request, res: Response) { + const auth = res.locals.auth as IJwt; + + const me = await UserService.id(auth.userId); + + if (!me) { + throw new NotAuthenticated(); + } + + return res.status(200).json({ id: me.id, email: me.email }); + } + + @Get("@me/projects") + @Middleware([isAuthenticated]) + public async meProjects(req: Request, res: Response) { + const auth = res.locals.auth as IJwt; + + const projects = await UserService.projects(auth.userId); + + return res.status(200).json(projects); + } +} diff --git a/packages/api/src/controllers/Webhooks/Incoming/SNS.ts b/packages/api/src/controllers/Webhooks/Incoming/SNS.ts new file mode 100644 index 0000000..6b0413a --- /dev/null +++ b/packages/api/src/controllers/Webhooks/Incoming/SNS.ts @@ -0,0 +1,191 @@ +import { Controller, Post } from "@overnightjs/core"; +import type { Event } from "@prisma/client"; +import type { Request, Response } from "express"; +import signale from "signale"; +import { prisma } from "../../../database/prisma"; +import { ActionService } from "../../../services/ActionService"; +import { ProjectService } from "../../../services/ProjectService"; + +const eventMap = { + Bounce: "BOUNCED", + Delivery: "DELIVERED", + Open: "OPENED", + Complaint: "COMPLAINT", + Click: "CLICKED", +} as const; + +@Controller("sns") +export class SNSWebhook { + @Post() + public async receiveSNSWebhook(req: Request, res: Response) { + try { + const body = JSON.parse(req.body.Message); + + const email = await prisma.email.findUnique({ + where: { messageId: body.mail.messageId }, + include: { + contact: true, + action: { include: { template: { include: { events: true } } } }, + campaign: { include: { events: true } }, + }, + }); + + if (!email) { + return res.status(200).json({}); + } + + const project = await ProjectService.id(email.contact.projectId); + + if (!project) { + return res.status(200).json({ success: false }); + } + + // The email was a transactional email + if (email.projectId) { + if (body.eventType === "Click") { + signale.success( + `Click received for ${email.contact.email} from ${project.name}`, + ); + await prisma.click.create({ + data: { emailId: email.id, link: body.click.link }, + }); + } + + if (body.eventType === "Complaint") { + signale.warn( + `Complaint received for ${email.contact.email} from ${project.name}`, + ); + } + + if (body.eventType === "Bounce") { + signale.warn( + `Bounce received for ${email.contact.email} from ${project.name}`, + ); + } + + await prisma.email.update({ + where: { messageId: body.mail.messageId }, + data: { + status: + eventMap[ + body.eventType as "Bounce" | "Delivery" | "Open" | "Complaint" + ], + }, + }); + + return res.status(200).json({ success: true }); + } + + if (body.eventType === "Complaint" || body.eventType === "Bounce") { + signale.warn( + `${body.eventType === "Complaint" ? "Complaint" : "Bounce"} received for ${email.contact.email} from ${project.name}`, + ); + + await prisma.email.update({ + where: { messageId: body.mail.messageId }, + data: { status: eventMap[body.eventType as "Bounce" | "Complaint"] }, + }); + + await prisma.contact.update({ + where: { id: email.contactId }, + data: { subscribed: false }, + }); + + return res.status(200).json({ success: true }); + } + + if (body.eventType === "Click") { + signale.success( + `Click received for ${email.contact.email} from ${project.name}`, + ); + + await prisma.click.create({ + data: { emailId: email.id, link: body.click.link }, + }); + + return res.status(200).json({ success: true }); + } + + let event: Event | undefined; + + if (email.action) { + event = email.action.template.events.find((e) => + e.name.includes( + (body.eventType as + | "Bounce" + | "Delivery" + | "Open" + | "Complaint" + | "Click") === "Delivery" + ? "delivered" + : "opened", + ), + ); + } + + if (email.campaign) { + event = email.campaign.events.find((e) => + e.name.includes( + (body.eventType as + | "Bounce" + | "Delivery" + | "Open" + | "Complaint" + | "Click") === "Delivery" + ? "delivered" + : "opened", + ), + ); + } + + if (!event) { + return res.status(200).json({ success: false }); + } + + switch (body.eventType as "Delivery" | "Open") { + case "Delivery": + signale.success( + `Delivery received for ${email.contact.email} from ${project.name}`, + ); + await prisma.email.update({ + where: { messageId: body.mail.messageId }, + data: { status: "DELIVERED" }, + }); + + await prisma.trigger.create({ + data: { contactId: email.contactId, eventId: event.id }, + }); + + break; + case "Open": + signale.success( + `Open received for ${email.contact.email} from ${project.name}`, + ); + await prisma.email.update({ + where: { messageId: body.mail.messageId }, + data: { status: "OPENED" }, + }); + await prisma.trigger.create({ + data: { contactId: email.contactId, eventId: event.id }, + }); + + break; + } + + if (email.action) { + void ActionService.trigger({ event, contact: email.contact, project }); + } + } catch (e) { + if (req.body.SubscribeURL) { + signale.info("--------------"); + signale.info("SNS Topic Confirmation URL:"); + signale.info(req.body.SubscribeURL); + signale.info("--------------"); + } else { + signale.error(e); + } + } + + return res.status(200).json({ success: true }); + } +} diff --git a/packages/api/src/controllers/Webhooks/Incoming/index.ts b/packages/api/src/controllers/Webhooks/Incoming/index.ts new file mode 100644 index 0000000..f733371 --- /dev/null +++ b/packages/api/src/controllers/Webhooks/Incoming/index.ts @@ -0,0 +1,6 @@ +import {ChildControllers, Controller} from '@overnightjs/core'; +import {SNSWebhook} from './SNS'; + +@Controller('incoming') +@ChildControllers([new SNSWebhook()]) +export class IncomingWebhooks {} diff --git a/packages/api/src/controllers/Webhooks/index.ts b/packages/api/src/controllers/Webhooks/index.ts new file mode 100644 index 0000000..1393ecc --- /dev/null +++ b/packages/api/src/controllers/Webhooks/index.ts @@ -0,0 +1,6 @@ +import {ChildControllers, Controller} from '@overnightjs/core'; +import {IncomingWebhooks} from './Incoming'; + +@Controller('webhooks') +@ChildControllers([new IncomingWebhooks()]) +export class Webhooks {} diff --git a/packages/api/src/controllers/v1/Actions.ts b/packages/api/src/controllers/v1/Actions.ts new file mode 100644 index 0000000..14f1b9d --- /dev/null +++ b/packages/api/src/controllers/v1/Actions.ts @@ -0,0 +1,270 @@ +import { + Controller, + Delete, + Get, + Middleware, + Post, + Put, +} from "@overnightjs/core"; +import { ActionSchemas, UtilitySchemas } from "@plunk/shared"; +import type { Request, Response } from "express"; +import { prisma } from "../../database/prisma"; +import { NotFound } from "../../exceptions"; +import { + type IJwt, + type ISecret, + isAuthenticated, + isValidSecretKey, +} from "../../middleware/auth"; +import { ActionService } from "../../services/ActionService"; +import { EventService } from "../../services/EventService"; +import { MembershipService } from "../../services/MembershipService"; +import { ProjectService } from "../../services/ProjectService"; +import { TemplateService } from "../../services/TemplateService"; +import { Keys } from "../../services/keys"; +import { redis } from "../../services/redis"; + +@Controller("actions") +export class Actions { + @Get(":id") + @Middleware([isAuthenticated]) + public async getActionById(req: Request, res: Response) { + const { id } = UtilitySchemas.id.parse(req.params); + + const { userId } = res.locals.auth as IJwt; + + const action = await ActionService.id(id); + + if (!action) { + throw new NotFound("action"); + } + + const isMember = await MembershipService.isMember(action.projectId, userId); + + if (!isMember) { + throw new NotFound("action"); + } + + return res.status(200).json(action); + } + + @Get(":id/related") + @Middleware([isAuthenticated]) + public async getRelatedActionsById(req: Request, res: Response) { + const { id } = UtilitySchemas.id.parse(req.params); + + const { userId } = res.locals.auth as IJwt; + + const action = await ActionService.id(id); + + if (!action) { + throw new NotFound("action"); + } + + const isMember = await MembershipService.isMember(action.projectId, userId); + + if (!isMember) { + throw new NotFound("action"); + } + + const related = await ActionService.related(id); + + return res.status(200).json(related); + } + + @Post() + @Middleware([isValidSecretKey]) + public async createAction(req: Request, res: Response) { + const { sk } = res.locals.auth as ISecret; + + const project = await ProjectService.secret(sk); + + if (!project) { + throw new NotFound("project"); + } + + const { + name, + runOnce, + delay, + template: templateId, + events, + notevents, + } = ActionSchemas.create.parse(req.body); + + const template = await TemplateService.id(templateId); + + if (!template) { + throw new NotFound("template"); + } + + const action = await prisma.action.create({ + data: { + projectId: project.id, + name, + runOnce, + delay, + templateId: template.id, + }, + }); + + await Promise.all([ + events.map(async (e: string) => { + const event = await EventService.id(e); + + if (!event) { + throw new NotFound("event"); + } + + if (event.projectId !== project.id) { + throw new NotFound("event"); + } + + await prisma.action.update({ + where: { id: action.id }, + data: { events: { connect: { id: event.id } } }, + }); + }), + notevents.map(async (e: string) => { + const event = await EventService.id(e); + + if (!event) { + throw new NotFound("event"); + } + + if (event.projectId !== project.id) { + throw new NotFound("event"); + } + + await prisma.action.update({ + where: { id: action.id }, + data: { notevents: { connect: { id: event.id } } }, + }); + }), + ]); + + await redis.del(Keys.Action.id(action.id)); + await redis.del(Keys.Project.actions(project.id)); + + return res.status(200).json(action); + } + + @Put() + @Middleware([isValidSecretKey]) + public async updateAction(req: Request, res: Response) { + const { sk } = res.locals.auth as ISecret; + + const project = await ProjectService.secret(sk); + + if (!project) { + throw new NotFound("project"); + } + + const { + id, + template: templateId, + events, + notevents, + name, + runOnce, + delay, + } = ActionSchemas.update.parse(req.body); + + let action = await ActionService.id(id); + + if (!action || action.projectId !== project.id) { + throw new NotFound("action"); + } + + const template = await TemplateService.id(templateId); + + if (!template || template.projectId !== project.id) { + throw new NotFound("template"); + } + + const actionEvents = await prisma.action.findUnique({ + where: { id }, + include: { events: true, notevents: true }, + }); + + action = await prisma.action.update({ + where: { id }, + data: { + name, + runOnce, + delay, + templateId, + events: { disconnect: actionEvents?.events.map((e) => ({ id: e.id })) }, + notevents: { + disconnect: actionEvents?.notevents.map((e) => ({ id: e.id })), + }, + }, + include: { + events: true, + notevents: true, + triggers: true, + emails: true, + template: true, + }, + }); + + await Promise.all([ + events.map(async (e: string) => { + const event = await EventService.id(e); + + if (!event || event.projectId !== project.id) { + throw new NotFound("event"); + } + + await prisma.action.update({ + where: { id }, + data: { events: { connect: { id: event.id } } }, + }); + }), + notevents.map(async (e: string) => { + const event = await EventService.id(e); + + if (!event || event.projectId !== project.id) { + throw new NotFound("event"); + } + + await prisma.action.update({ + where: { id }, + data: { notevents: { connect: { id: event.id } } }, + }); + }), + ]); + + await redis.del(Keys.Action.id(action.id)); + await redis.del(Keys.Project.actions(project.id)); + + return res.status(200).json(action); + } + + @Delete() + @Middleware([isValidSecretKey]) + public async deleteAction(req: Request, res: Response) { + const { sk } = res.locals.auth as ISecret; + + const project = await ProjectService.secret(sk); + + if (!project) { + throw new NotFound("project"); + } + + const { id } = UtilitySchemas.id.parse(req.body); + + const action = await ActionService.id(id); + + if (!action || action.projectId !== project.id) { + throw new NotFound("action"); + } + + await prisma.action.delete({ where: { id } }); + + await redis.del(Keys.Action.id(action.id)); + await redis.del(Keys.Project.actions(project.id)); + + return res.status(200).json(action); + } +} diff --git a/packages/api/src/controllers/v1/Campaigns.ts b/packages/api/src/controllers/v1/Campaigns.ts new file mode 100644 index 0000000..cdb704a --- /dev/null +++ b/packages/api/src/controllers/v1/Campaigns.ts @@ -0,0 +1,347 @@ +import { + Controller, + Delete, + Get, + Middleware, + Post, + Put, +} from "@overnightjs/core"; +import { CampaignSchemas, UtilitySchemas } from "@plunk/shared"; +import dayjs from "dayjs"; +import type { Request, Response } from "express"; +import { prisma } from "../../database/prisma"; +import { HttpException, NotFound } from "../../exceptions"; +import { + type IJwt, + type ISecret, + isAuthenticated, + isValidSecretKey, +} from "../../middleware/auth"; +import { CampaignService } from "../../services/CampaignService"; +import { EmailService } from "../../services/EmailService"; +import { MembershipService } from "../../services/MembershipService"; +import { ProjectService } from "../../services/ProjectService"; +import { Keys } from "../../services/keys"; +import { redis } from "../../services/redis"; + +@Controller("campaigns") +export class Campaigns { + @Get(":id") + @Middleware([isAuthenticated]) + public async getCampaignById(req: Request, res: Response) { + const { id } = UtilitySchemas.id.parse(req.params); + + const { userId } = res.locals.auth as IJwt; + + const campaign = await CampaignService.id(id); + + if (!campaign) { + throw new NotFound("campaign"); + } + + const isMember = await MembershipService.isMember( + campaign.projectId, + userId, + ); + + if (!isMember) { + throw new NotFound("campaign"); + } + + return res.status(200).json(campaign); + } + + @Post("send") + @Middleware([isValidSecretKey]) + public async sendCampaign(req: Request, res: Response) { + const { sk } = res.locals.auth as ISecret; + + const project = await ProjectService.secret(sk); + + if (!project) { + throw new NotFound("project"); + } + + const { id, live, delay: userDelay } = CampaignSchemas.send.parse(req.body); + + const campaign = await CampaignService.id(id); + + if (!campaign || campaign.projectId !== project.id) { + throw new NotFound("campaign"); + } + + if (live) { + if (campaign.recipients.length === 0) { + throw new HttpException(400, "No recipients found"); + } + + await prisma.campaign.update({ + where: { id: campaign.id }, + data: { status: "DELIVERED", delivered: new Date() }, + }); + + await prisma.event.createMany({ + data: [ + { + projectId: project.id, + name: `${campaign.subject + .toLowerCase() + .replace(/[.,/#!$%^&*;:{}=\-_`~()]/g, "") + .replace(/ /g, "-")}-campaign-delivered`, + campaignId: campaign.id, + }, + { + projectId: project.id, + name: `${campaign.subject + .toLowerCase() + .replace(/[.,/#!$%^&*;:{}=\-_`~()]/g, "") + .replace(/ /g, "-")}-campaign-opened`, + campaignId: campaign.id, + }, + ], + }); + + let delay = userDelay ?? 0; + + const tasks = campaign.recipients.map((r, index) => { + if (index % 80 === 0) { + delay += 1; + } + + return { + campaignId: campaign.id, + contactId: r.id, + runBy: dayjs().add(delay, "minutes").toDate(), + }; + }); + + await prisma.task.createMany({ data: tasks }); + } else { + const members = await ProjectService.memberships(project.id); + + await EmailService.send({ + from: { + name: project.from ?? project.name, + email: + project.verified && project.email + ? project.email + : "no-reply@useplunk.dev", + }, + to: members.map((m) => m.email), + content: { + subject: `[Plunk Campaign Test] ${campaign.subject}`, + html: EmailService.compile({ + content: campaign.body, + footer: { + unsubscribe: false, + }, + contact: { + id: "", + }, + project: { + name: project.name, + }, + }), + }, + }); + } + + await redis.del(Keys.Campaign.id(campaign.id)); + await redis.del(Keys.Project.campaigns(project.id)); + + return res.status(200).json({}); + } + + @Post("duplicate") + @Middleware([isValidSecretKey]) + public async duplicateCampaign(req: Request, res: Response) { + const { sk } = res.locals.auth as ISecret; + + const project = await ProjectService.secret(sk); + + if (!project) { + throw new NotFound("project"); + } + + const { id } = UtilitySchemas.id.parse(req.body); + + const campaign = await CampaignService.id(id); + + if (!campaign) { + throw new NotFound("campaign"); + } + + const duplicatedCampaign = await prisma.campaign.create({ + data: { + projectId: project.id, + subject: campaign.subject, + body: campaign.body, + style: campaign.style, + }, + }); + + await redis.del(Keys.Campaign.id(campaign.id)); + await redis.del(Keys.Project.campaigns(project.id)); + + return res.status(200).json(duplicatedCampaign); + } + + @Post() + @Middleware([isValidSecretKey]) + public async createCampaign(req: Request, res: Response) { + const { sk } = res.locals.auth as ISecret; + + const project = await ProjectService.secret(sk); + + if (!project) { + throw new NotFound("project"); + } + + let { subject, body, recipients, style } = CampaignSchemas.create.parse( + req.body, + ); + + if (recipients.length === 1 && recipients[0] === "all") { + const projectContacts = await prisma.contact.findMany({ + where: { projectId: project.id, subscribed: true }, + select: { id: true }, + }); + + recipients = projectContacts.map((c) => c.id); + } + + const campaign = await prisma.campaign.create({ + data: { + projectId: project.id, + subject, + body, + style, + }, + }); + + const chunkSize = 500; + for (let i = 0; i < recipients.length; i += chunkSize) { + const chunk = recipients.slice(i, i + chunkSize); + + await prisma.campaign.update({ + where: { id: campaign.id }, + data: { + recipients: { + connect: chunk.map((r: string) => ({ id: r })), + }, + }, + }); + } + + await redis.del(Keys.Campaign.id(campaign.id)); + await redis.del(Keys.Project.campaigns(project.id)); + + return res.status(200).json(campaign); + } + + @Put() + @Middleware([isValidSecretKey]) + public async updateCampaign(req: Request, res: Response) { + const { sk } = res.locals.auth as ISecret; + + const project = await ProjectService.secret(sk); + + if (!project) { + throw new NotFound("project"); + } + + // eslint-disable-next-line prefer-const + let { id, subject, body, recipients, style } = CampaignSchemas.update.parse( + req.body, + ); + + if (recipients.length === 1 && recipients[0] === "all") { + const projectContacts = await prisma.contact.findMany({ + where: { projectId: project.id, subscribed: true }, + select: { id: true }, + }); + + recipients = projectContacts.map((c) => c.id); + } + + let campaign = await CampaignService.id(id); + + if (!campaign || campaign.projectId !== project.id) { + throw new NotFound("campaign"); + } + + campaign = await prisma.campaign.update({ + where: { id }, + data: { + subject, + body, + style, + }, + include: { + recipients: { select: { id: true } }, + emails: { + select: { + id: true, + status: true, + contact: { select: { id: true, email: true } }, + }, + }, + }, + }); + + await prisma.campaign.update({ + where: { id }, + data: { + recipients: { + set: [], + }, + }, + }); + + const chunkSize = 500; + for (let i = 0; i < recipients.length; i += chunkSize) { + const chunk = recipients.slice(i, i + chunkSize); + + await prisma.campaign.update({ + where: { id }, + data: { + recipients: { + connect: chunk.map((r: string) => ({ id: r })), + }, + }, + }); + } + + await redis.del(Keys.Campaign.id(campaign.id)); + await redis.del(Keys.Project.campaigns(project.id)); + + return res.status(200).json(campaign); + } + + @Delete() + @Middleware([isValidSecretKey]) + public async deleteCampaign(req: Request, res: Response) { + const { sk } = res.locals.auth as ISecret; + + const project = await ProjectService.secret(sk); + + if (!project) { + throw new NotFound("project"); + } + + const { id } = UtilitySchemas.id.parse(req.body); + + const campaign = await CampaignService.id(id); + + if (!campaign || campaign.projectId !== project.id) { + throw new NotFound("campaign"); + } + + await prisma.campaign.delete({ where: { id } }); + + await redis.del(Keys.Campaign.id(campaign.id)); + await redis.del(Keys.Project.campaigns(project.id)); + + return res.status(200).json(campaign); + } +} diff --git a/packages/api/src/controllers/v1/Contacts.ts b/packages/api/src/controllers/v1/Contacts.ts new file mode 100644 index 0000000..95f660c --- /dev/null +++ b/packages/api/src/controllers/v1/Contacts.ts @@ -0,0 +1,334 @@ +import { + Controller, + Delete, + Get, + Middleware, + Post, + Put, +} from "@overnightjs/core"; +import { ContactSchemas, UtilitySchemas } from "@plunk/shared"; +import type { Request, Response } from "express"; +import z from "zod"; +import { prisma } from "../../database/prisma"; +import { HttpException, NotFound } from "../../exceptions"; +import { + type IKey, + type ISecret, + isValidKey, + isValidSecretKey, +} from "../../middleware/auth"; +import { ActionService } from "../../services/ActionService"; +import { ContactService } from "../../services/ContactService"; +import { EventService } from "../../services/EventService"; +import { ProjectService } from "../../services/ProjectService"; +import { Keys } from "../../services/keys"; +import { redis } from "../../services/redis"; + +@Controller("contacts") +export class Contacts { + @Get("count") + @Middleware([isValidKey]) + public async getContactCount(req: Request, res: Response) { + const { key } = res.locals.auth as IKey; + + const project = await ProjectService.key(key); + + if (!project) { + throw new NotFound("project"); + } + + const count = await ProjectService.contacts.count(project.id); + + return res.status(200).json({ count }); + } + + @Get(":id") + public async getContactById(req: Request, res: Response) { + const { id } = UtilitySchemas.id.parse(req.params); + const { withProject } = z + .object({ + withProject: z + .boolean() + .default(false) + .or(z.string().transform((s) => s === "true")), + }) + .parse(req.query); + + const contact = await ContactService.id(id); + + if (!contact) { + throw new NotFound("contact"); + } + + if (withProject) { + const project = await ProjectService.id(contact.projectId); + + if (!project) { + throw new NotFound("project"); + } + + return res.status(200).json({ + ...contact, + project: { name: project.name, public: project.public }, + }); + } + return res.status(200).json(contact); + } + + @Get() + @Middleware([isValidSecretKey]) + public async getContacts(req: Request, res: Response) { + const { sk } = res.locals.auth as ISecret; + + const project = await ProjectService.secret(sk); + + if (!project) { + throw new NotFound("project"); + } + + const contacts = await ProjectService.contacts.get(project.id); + + return res.status(200).json( + contacts?.map((c) => { + return { + id: c.id, + email: c.email, + subscribed: c.subscribed, + data: c.data, + createdAt: c.createdAt, + updatedAt: c.updatedAt, + }; + }), + ); + } + + @Post("unsubscribe") + @Middleware([isValidKey]) + public async unsubscribe(req: Request, res: Response) { + const { key } = res.locals.auth as IKey; + + const project = await ProjectService.key(key); + + if (!project) { + throw new NotFound("project"); + } + + const { id } = UtilitySchemas.id.parse(req.body); + + const contact = await ContactService.id(id); + + if (!contact || contact.projectId !== project.id) { + throw new NotFound("contact"); + } + + await prisma.contact.update({ + where: { id }, + data: { subscribed: false }, + }); + + let event = await EventService.event(project.id, "unsubscribe"); + + if (!event) { + event = await prisma.event.create({ + data: { name: "unsubscribe", projectId: project.id }, + }); + + await redis.del(Keys.Project.events(project.id, true)); + await redis.del(Keys.Project.events(project.id, false)); + await redis.del(Keys.Event.event(project.id, event.name)); + await redis.del(Keys.Event.id(event.id)); + } + + await prisma.trigger.create({ + data: { eventId: event.id, contactId: contact.id }, + }); + await redis.del(Keys.Contact.id(contact.id)); + + await ActionService.trigger({ event, contact, project }); + + await redis.del(Keys.Project.contacts(project.id)); + await redis.del(Keys.Contact.id(contact.id)); + await redis.del(Keys.Contact.email(project.id, contact.email)); + + return res + .status(200) + .json({ success: true, contact: contact.id, subscribed: false }); + } + + @Post("subscribe") + @Middleware([isValidKey]) + public async subscribe(req: Request, res: Response) { + const { key } = res.locals.auth as IKey; + + const project = await ProjectService.key(key); + + if (!project) { + throw new NotFound("project"); + } + + const { id } = UtilitySchemas.id.parse(req.body); + + const contact = await ContactService.id(id); + + if (!contact || contact.projectId !== project.id) { + throw new NotFound("contact"); + } + + await prisma.contact.update({ + where: { id }, + data: { subscribed: true }, + }); + + let event = await EventService.event(project.id, "subscribe"); + + if (!event) { + event = await prisma.event.create({ + data: { name: "subscribe", projectId: project.id }, + }); + + await redis.del(Keys.Project.events(project.id, true)); + await redis.del(Keys.Project.events(project.id, false)); + await redis.del(Keys.Event.event(project.id, event.name)); + await redis.del(Keys.Event.id(event.id)); + } + + await prisma.trigger.create({ + data: { eventId: event.id, contactId: contact.id }, + }); + await redis.del(Keys.Contact.id(contact.id)); + + await ActionService.trigger({ event, contact, project }); + + await redis.del(Keys.Project.contacts(project.id)); + await redis.del(Keys.Contact.id(contact.id)); + await redis.del(Keys.Contact.email(project.id, contact.email)); + + return res + .status(200) + .json({ success: true, contact: contact.id, subscribed: true }); + } + + @Post() + @Middleware([isValidSecretKey]) + public async createContact(req: Request, res: Response) { + const { sk } = res.locals.auth as ISecret; + + const project = await ProjectService.secret(sk); + + if (!project) { + throw new NotFound("project"); + } + + const { email, subscribed, data } = ContactSchemas.create.parse(req.body); + + let contact = await ContactService.email(project.id, email); + + if (contact) { + throw new HttpException(409, "Contact already exists"); + } + + contact = await prisma.contact.create({ + data: { + projectId: project.id, + email, + subscribed, + data: data ? JSON.stringify(data) : null, + }, + }); + + await redis.del(Keys.Project.contacts(project.id)); + await redis.del(Keys.Contact.id(contact.id)); + await redis.del(Keys.Contact.email(project.id, email)); + + return res.status(200).json({ + success: true, + id: contact.id, + email: contact.email, + subscribed: contact.subscribed, + data: contact.data, + createdAt: contact.createdAt, + updatedAt: contact.updatedAt, + }); + } + + @Put() + @Middleware([isValidSecretKey]) + public async updateContact(req: Request, res: Response) { + const { sk } = res.locals.auth as ISecret; + + const project = await ProjectService.secret(sk); + + if (!project) { + throw new NotFound("project"); + } + + const { id, email, subscribed, data } = ContactSchemas.update.parse( + req.body, + ); + + let contact = await ContactService.id(id); + + if (!contact || contact.projectId !== project.id) { + throw new NotFound("contact"); + } + + contact = await prisma.contact.update({ + where: { id }, + data: { email, subscribed, data: data ? JSON.stringify(data) : null }, + include: { + triggers: { include: { event: true, action: true } }, + emails: { where: { subject: { not: null } } }, + }, + }); + + await redis.del(Keys.Project.contacts(project.id)); + await redis.del(Keys.Contact.id(contact.id)); + await redis.del(Keys.Contact.email(project.id, email)); + + return res.status(200).json({ + success: true, + id: contact.id, + email: contact.email, + subscribed: contact.subscribed, + data: contact.data, + createdAt: contact.createdAt, + updatedAt: contact.updatedAt, + }); + } + + @Delete() + @Middleware([isValidSecretKey]) + public async deleteContact(req: Request, res: Response) { + const { sk } = res.locals.auth as ISecret; + + const project = await ProjectService.secret(sk); + + if (!project) { + throw new NotFound("project"); + } + + const { id } = UtilitySchemas.id.parse(req.body); + + const contact = await ContactService.id(id); + + if (!contact || contact.projectId !== project.id) { + throw new NotFound("contact"); + } + + await prisma.contact.delete({ where: { id } }); + + await redis.del(Keys.Project.contacts(project.id)); + await redis.del(Keys.Contact.id(contact.id)); + await redis.del(Keys.Contact.email(project.id, contact.email)); + + return res.status(200).json({ + success: true, + id: contact.id, + email: contact.email, + subscribed: contact.subscribed, + data: contact.data, + createdAt: contact.createdAt, + updatedAt: contact.updatedAt, + }); + } +} diff --git a/packages/api/src/controllers/v1/Events.ts b/packages/api/src/controllers/v1/Events.ts new file mode 100644 index 0000000..d10c97d --- /dev/null +++ b/packages/api/src/controllers/v1/Events.ts @@ -0,0 +1,39 @@ +import { Controller, Delete, Middleware } from "@overnightjs/core"; +import { UtilitySchemas } from "@plunk/shared"; +import type { Request, Response } from "express"; +import { prisma } from "../../database/prisma"; +import { NotFound } from "../../exceptions"; +import { type ISecret, isValidSecretKey } from "../../middleware/auth"; +import { EventService } from "../../services/EventService"; +import { ProjectService } from "../../services/ProjectService"; +import { Keys } from "../../services/keys"; +import { redis } from "../../services/redis"; + +@Controller("events") +export class Events { + @Delete() + @Middleware([isValidSecretKey]) + public async deleteEvent(req: Request, res: Response) { + const { sk } = res.locals.auth as ISecret; + + const project = await ProjectService.secret(sk); + + if (!project) { + throw new NotFound("project"); + } + + const { id } = UtilitySchemas.id.parse(req.body); + + const event = await EventService.id(id); + + if (!event || event.projectId !== project.id) { + throw new NotFound("event"); + } + + await prisma.event.delete({ where: { id } }); + + await redis.del(Keys.Event.id(id)); + + return res.status(200).json(event); + } +} diff --git a/packages/api/src/controllers/v1/Templates.ts b/packages/api/src/controllers/v1/Templates.ts new file mode 100644 index 0000000..4160b78 --- /dev/null +++ b/packages/api/src/controllers/v1/Templates.ts @@ -0,0 +1,253 @@ +import { randomBytes } from "node:crypto"; +import { + Controller, + Delete, + Get, + Middleware, + Post, + Put, +} from "@overnightjs/core"; +import { TemplateSchemas, UtilitySchemas } from "@plunk/shared"; +import type { Request, Response } from "express"; +import { prisma } from "../../database/prisma"; +import { NotAllowed, NotFound } from "../../exceptions"; +import { + type IJwt, + type ISecret, + isAuthenticated, + isValidSecretKey, +} from "../../middleware/auth"; +import { MembershipService } from "../../services/MembershipService"; +import { ProjectService } from "../../services/ProjectService"; +import { TemplateService } from "../../services/TemplateService"; +import { Keys } from "../../services/keys"; +import { redis } from "../../services/redis"; + +@Controller("templates") +export class Templates { + @Get(":id") + @Middleware([isAuthenticated]) + public async getTemplateById(req: Request, res: Response) { + const { id } = UtilitySchemas.id.parse(req.params); + + const { userId } = res.locals.auth as IJwt; + + const template = await TemplateService.id(id); + + if (!template) { + throw new NotFound("template"); + } + + const isMember = await MembershipService.isMember( + template.projectId, + userId, + ); + + if (!isMember) { + throw new NotFound("template"); + } + + return res.status(200).json(template); + } + + @Post("duplicate") + @Middleware([isValidSecretKey]) + public async duplicateTemplate(req: Request, res: Response) { + const { sk } = res.locals.auth as ISecret; + + const project = await ProjectService.secret(sk); + + if (!project) { + throw new NotFound("project"); + } + + const { id } = UtilitySchemas.id.parse(req.body); + + const template = await TemplateService.id(id); + + if (!template || template.projectId !== project.id) { + throw new NotFound("template"); + } + + const duplicatedTemplate = await prisma.template.create({ + data: { + projectId: project.id, + subject: template.subject, + body: template.body, + type: template.type, + style: template.style, + }, + }); + + await prisma.event.createMany({ + data: [ + { + projectId: project.id, + name: `${duplicatedTemplate.subject + .toLowerCase() + .replace(/[.,/#!$%^&*;:{}=\-_`~()]/g, "") + .replace(/ /g, "-")}-template-delivered`, + templateId: template.id, + }, + { + projectId: project.id, + name: `${duplicatedTemplate.subject + .toLowerCase() + .replace(/[.,/#!$%^&*;:{}=\-_`~()]/g, "") + .replace(/ /g, "-")}-template-opened`, + templateId: template.id, + }, + ], + }); + + await redis.del(Keys.Project.templates(project.id)); + await redis.del(Keys.Template.id(template.id)); + + return res.status(200).json(template); + } + + @Post() + @Middleware([isValidSecretKey]) + public async createTemplate(req: Request, res: Response) { + const { sk } = res.locals.auth as ISecret; + + const project = await ProjectService.secret(sk); + + if (!project) { + throw new NotFound("project"); + } + + const { subject, body, type, style } = TemplateSchemas.create.parse( + req.body, + ); + + const template = await prisma.template.create({ + data: { + projectId: project.id, + subject, + body, + type, + style, + }, + }); + + await prisma.event.createMany({ + data: [ + { + projectId: project.id, + name: `${subject + .toLowerCase() + .replace(/[.,/#!$%^&*;:{}=\-_`~()]/g, "") + .replace(/ /g, "-")}-template-delivered`, + templateId: template.id, + }, + { + projectId: project.id, + name: `${subject + .toLowerCase() + .replace(/[.,/#!$%^&*;:{}=\-_`~()]/g, "") + .replace(/ /g, "-")}-template-opened`, + templateId: template.id, + }, + ], + }); + + await redis.del(Keys.Project.templates(project.id)); + await redis.del(Keys.Template.id(template.id)); + + return res.status(200).json(template); + } + + @Put() + @Middleware([isValidSecretKey]) + public async updateTemplate(req: Request, res: Response) { + const { sk } = res.locals.auth as ISecret; + + const project = await ProjectService.secret(sk); + + if (!project) { + throw new NotFound("project"); + } + + const { id, subject, body, type, style } = TemplateSchemas.update.parse( + req.body, + ); + + let template = await TemplateService.id(id); + + if (!template || template.projectId !== project.id) { + throw new NotFound("template"); + } + + template = await prisma.template.update({ + where: { id }, + data: { subject, body, type, style }, + include: { + actions: true, + }, + }); + + const events = await prisma.event.findMany({ + where: { templateId: template.id }, + }); + + await Promise.all( + events.map(async (e) => { + await prisma.event.update({ + where: { id: e.id }, + data: { + name: e.name.includes("delivered") + ? `${subject + .toLowerCase() + .replace(/[.,/#!$%^&*;:{}=\-_`~()]/g, "") + .replace(/ /g, "-")}-template-delivered` + : `${subject + .toLowerCase() + .replace(/[.,/#!$%^&*;:{}=\-_`~()]/g, "") + .replace(/ /g, "-")}-template-opened`, + }, + }); + }), + ); + + await redis.del(Keys.Project.templates(project.id)); + await redis.del(Keys.Template.id(template.id)); + + return res.status(200).json(template); + } + + @Delete() + @Middleware([isValidSecretKey]) + public async deleteTemplate(req: Request, res: Response) { + const { sk } = res.locals.auth as ISecret; + + const project = await ProjectService.secret(sk); + + if (!project) { + throw new NotFound("project"); + } + + const { id } = UtilitySchemas.id.parse(req.body); + + const template = await TemplateService.id(id); + + if (!template || template.projectId !== project.id) { + throw new NotFound("template"); + } + + const actions = await TemplateService.actions(id); + + if (actions && actions.length > 0) { + throw new NotAllowed( + "This template is being used by an action. Unlink the action before deleting the template.", + ); + } + + await prisma.template.delete({ where: { id } }); + + await redis.del(Keys.Project.templates(project.id)); + await redis.del(Keys.Template.id(template.id)); + + return res.status(200).json(template); + } +} diff --git a/packages/api/src/controllers/v1/index.ts b/packages/api/src/controllers/v1/index.ts new file mode 100644 index 0000000..546a635 --- /dev/null +++ b/packages/api/src/controllers/v1/index.ts @@ -0,0 +1,298 @@ +import { + ChildControllers, + Controller, + Middleware, + Post, +} from "@overnightjs/core"; +import { EventSchemas } from "@plunk/shared"; +import dayjs from "dayjs"; +import type { Request, Response } from "express"; +import signale from "signale"; +import { prisma } from "../../database/prisma"; +import { HttpException, NotAllowed } from "../../exceptions"; +import { + type IKey, + type ISecret, + isValidKey, + isValidSecretKey, +} from "../../middleware/auth"; +import { ActionService } from "../../services/ActionService"; +import { ContactService } from "../../services/ContactService"; +import { EmailService } from "../../services/EmailService"; +import { EventService } from "../../services/EventService"; +import { ProjectService } from "../../services/ProjectService"; +import { Keys } from "../../services/keys"; +import { redis } from "../../services/redis"; +import { Actions } from "./Actions"; +import { Campaigns } from "./Campaigns"; +import { Contacts } from "./Contacts"; +import { Events } from "./Events"; +import { Templates } from "./Templates"; + +@Controller("v1") +@ChildControllers([ + new Actions(), + new Templates(), + new Campaigns(), + new Contacts(), + new Events(), +]) +export class V1 { + @Post() + @Post("track") + @Middleware([isValidKey]) + public async postEvent(req: Request, res: Response) { + const { key } = res.locals.auth as IKey; + + const project = await ProjectService.key(key); + + if (!project) { + throw new HttpException(401, "Incorrect Bearer token specified"); + } + + const result = EventSchemas.post.safeParse(req.body); + + if (!result.success) { + signale.warn( + `${project.name} tried tracking an event with invalid data: ${JSON.stringify(req.body)}`, + ); + if ("unionErrors" in result.error.issues[0]) { + throw new HttpException( + 400, + result.error.issues[0].unionErrors[0].errors[0].message, + ); + } + + throw new HttpException(400, result.error.issues[0].message); + } + + const { event: name, email, data, subscribed } = result.data; + + if (name === "subscribe" || name === "unsubscribe") { + throw new NotAllowed("subscribe & unsubscribe are reserved event names."); + } + + let event = await EventService.event(project.id, name); + + if (!event) { + event = await prisma.event.create({ + data: { name, projectId: project.id }, + }); + redis.set( + Keys.Event.event(project.id, event.name), + JSON.stringify(event), + ); + redis.set(Keys.Event.id(event.id), JSON.stringify(event)); + + redis.del(Keys.Project.events(project.id, true)); + redis.del(Keys.Project.events(project.id, false)); + } + + let contact = await ContactService.email(project.id, email); + + if (!contact) { + contact = await prisma.contact.create({ + data: { + email, + subscribed: subscribed ?? true, + projectId: project.id, + }, + }); + + redis.del(Keys.Contact.id(contact.id)); + redis.del(Keys.Contact.email(project.id, contact.email)); + } else { + if (subscribed && contact.subscribed !== subscribed) { + contact = await prisma.contact.update({ + where: { id: contact.id }, + data: { subscribed }, + }); + + redis.del(Keys.Contact.id(contact.id)); + redis.del(Keys.Contact.email(project.id, contact.email)); + } + } + + if (data) { + const givenUserData = Object.entries(data); + const userData = JSON.parse(contact.data ?? "{}"); + const dataToUpdate = JSON.parse(contact.data ?? "{}"); + + givenUserData.forEach(([key, value]) => { + userData[key] = value.value; + if (value.persistent) { + dataToUpdate[key] = value.value; + } + }); + + contact.data = JSON.stringify(userData); + + await prisma.contact.update({ + where: { id: contact.id }, + data: { data: JSON.stringify(dataToUpdate) }, + }); + } + + await prisma.trigger.create({ + data: { eventId: event.id, contactId: contact.id }, + }); + + void ActionService.trigger({ event, contact, project }); + + signale.success( + `${project.name} triggered ${event.name} for ${contact.email}`, + ); + + return res.status(200).json({ + success: true, + contact: contact.id, + event: event.id, + timestamp: dayjs().toISOString(), + }); + } + + @Post("send") + @Middleware([isValidSecretKey]) + public async send(req: Request, res: Response) { + const { sk } = res.locals.auth as ISecret; + + const project = await ProjectService.secret(sk); + + if (!project) { + throw new HttpException(401, "Incorrect Bearer token specified"); + } + + const result = EventSchemas.send.safeParse(req.body); + + if (!result.success) { + if ("unionErrors" in result.error.issues[0]) { + throw new HttpException( + 400, + result.error.issues[0].unionErrors[0].errors[0].message, + ); + } + + throw new HttpException(400, result.error.issues[0].message); + } + + const { from, name, reply, to, subject, body, subscribed, headers } = + result.data; + + if (!project.email || !project.verified) { + throw new HttpException( + 401, + "Verify your domain before you start sending", + ); + } + + if (from && from.split("@")[1] !== project.email?.split("@")[1]) { + throw new HttpException( + 401, + "Custom from address must be from a verified domain", + ); + } + + const emails: { + contact: { + id: string; + email: string; + }; + email: string; + }[] = []; + + for (const email of to) { + let contact = await ContactService.email(project.id, email); + + if (!contact) { + contact = await prisma.contact.create({ + data: { + email, + subscribed: subscribed ?? false, + projectId: project.id, + }, + }); + + redis.del(Keys.Contact.id(contact.id)); + redis.del(Keys.Contact.email(project.id, contact.email)); + } else { + if (subscribed && contact.subscribed !== subscribed) { + await prisma.contact.update({ + where: { id: contact.id }, + data: { subscribed }, + }); + redis.set( + Keys.Contact.email(project.id, contact.email), + JSON.stringify({ + ...contact, + subscribed, + }), + ); + redis.del(Keys.Contact.id(contact.id)); + } + } + + const { subject: enrichedSubject, body: enrichedBody } = + EmailService.format({ + subject, + body, + data: { + plunk_id: contact.id, + plunk_email: contact.email, + ...JSON.parse(contact.data ?? "{}"), + }, + }); + + const { messageId } = await EmailService.send({ + from: { + name: name ?? project.from ?? project.name, + email: from ?? project.email, + }, + reply: reply ?? from ?? project.email, + to: [email], + headers, + content: { + subject: enrichedSubject, + html: EmailService.compile({ + isHtml: true, + content: enrichedBody, + footer: { + unsubscribe: false, + }, + contact: { + id: contact.id, + }, + project: { + name: project.name, + }, + }), + }, + }); + + const createdEmail = await prisma.email.create({ + data: { + messageId, + subject, + body: enrichedBody, + contactId: contact.id, + projectId: project.id, + }, + }); + + emails.push({ + contact: { id: contact.id, email: contact.email }, + email: createdEmail.id, + }); + } + + redis.del(Keys.Project.emails(project.id)); + redis.del(Keys.Project.emails(project.id, { count: true })); + + signale.success( + `${project.name} sent a transactional email to ${to.join(", ")}`, + ); + + return res + .status(200) + .json({ success: true, emails, timestamp: dayjs().toISOString() }); + } +} diff --git a/packages/api/src/database/prisma.ts b/packages/api/src/database/prisma.ts new file mode 100644 index 0000000..233a2c2 --- /dev/null +++ b/packages/api/src/database/prisma.ts @@ -0,0 +1,3 @@ +import {PrismaClient} from '@prisma/client'; + +export const prisma = new PrismaClient(); diff --git a/packages/api/src/exceptions/index.ts b/packages/api/src/exceptions/index.ts new file mode 100644 index 0000000..52e5a82 --- /dev/null +++ b/packages/api/src/exceptions/index.ts @@ -0,0 +1,34 @@ +export class HttpException extends Error { + public constructor( + public readonly code: number, + message: string, + ) { + super(message); + } +} + +export class NotFound extends HttpException { + /** + * Construct a new NotFound exception + * @param resource The type of resource that was not found + */ + public constructor(resource: string) { + super(404, `That ${resource.toLowerCase()} was not found`); + } +} + +export class NotAllowed extends HttpException { + /** + * Construct a new NotAllowed exception + * @param msg + */ + public constructor(msg = 'You are not allowed to perform this action') { + super(403, msg); + } +} + +export class NotAuthenticated extends HttpException { + public constructor() { + super(401, 'You need to be authenticated to do this'); + } +} diff --git a/packages/api/src/middleware/auth.ts b/packages/api/src/middleware/auth.ts new file mode 100644 index 0000000..528a801 --- /dev/null +++ b/packages/api/src/middleware/auth.ts @@ -0,0 +1,177 @@ +import dayjs from "dayjs"; +import type { NextFunction, Request, Response } from "express"; +import jsonwebtoken from "jsonwebtoken"; +import { JWT_SECRET } from "../app/constants"; +import { HttpException, NotAuthenticated } from "../exceptions"; + +export interface IJwt { + type: "jwt"; + userId: string; +} + +export interface ISecret { + type: "secret"; + sk: string; +} + +export interface IKey { + type: "key"; + key: string; +} + +/** + * Middleware to check if this unsubscribe is authenticated on the dashboard + * @param req + * @param res + * @param next + */ +export const isAuthenticated = ( + req: Request, + res: Response, + next: NextFunction, +) => { + res.locals.auth = { type: "jwt", userId: parseJwt(req) }; + + next(); +}; + +/** + * Middleware to check if this request is signed with an API secret key + * @param req + * @param res + * @param next + */ +export const isValidSecretKey = ( + req: Request, + res: Response, + next: NextFunction, +) => { + res.locals.auth = { type: "secret", sk: parseBearer(req, "secret") }; + + next(); +}; + +export const isValidKey = (req: Request, res: Response, next: NextFunction) => { + res.locals.auth = { type: "key", key: parseBearer(req) }; + + next(); +}; + +export const jwt = { + /** + * Extracts a unsubscribe id from a jwt + * @param token The JWT token + */ + verify(token: string): string | null { + try { + const verified = jsonwebtoken.verify(token, JWT_SECRET) as { + id: string; + }; + return verified.id; + } catch (e) { + return null; + } + }, + /** + * Signs a JWT token + * @param id The unsubscribe's ID to sign into a jwt token + */ + sign(id: string): string { + return jsonwebtoken.sign({ id }, JWT_SECRET, { + expiresIn: "168h", + }); + }, + /** + * Find out when a JWT expires + * @param token The unsubscribe's jwt token + */ + expires(token: string): dayjs.Dayjs { + const { exp } = jsonwebtoken.verify(token, JWT_SECRET) as { + exp?: number; + }; + return dayjs(exp); + }, +}; + +/** + * Parse a unsubscribe's ID from the request JWT token + * @param request The express request object + */ +export function parseJwt(request: Request): string { + const token: string | undefined = request.cookies.token; + + if (!token) { + throw new NotAuthenticated(); + } + + const id = jwt.verify(token); + + if (!id) { + throw new NotAuthenticated(); + } + + return id; +} + +/** + * Parse a bearer token from the request headers + * @param request The express request object + * @param type + */ +export function parseBearer( + request: Request, + type?: "secret" | "public", +): string { + const bearer: string | undefined = request.headers.authorization; + + if (!bearer) { + throw new HttpException(401, "No authorization header passed"); + } + + if (!bearer.includes("Bearer")) { + throw new HttpException(401, "Please add Bearer in front of your API key"); + } + + const split = bearer.split(" "); + + if (!(split[0] === "Bearer") || split.length > 2) { + throw new HttpException( + 401, + "Your authorization header is malformed. Please pass your API key as Bearer sk_...", + ); + } + + if (!type && !split[1].startsWith("sk_") && !split[1].startsWith("pk_")) { + throw new HttpException( + 401, + "Your API key could not be parsed. API keys start with sk_ or pk_", + ); + } + + if (!type) { + return split[1]; + } + + if (type === "secret" && split[1].startsWith("pk_")) { + throw new HttpException( + 401, + "You attached a public key but this route may only be accessed with a secret key", + ); + } + + if (type === "secret" && !split[1].startsWith("sk_")) { + throw new HttpException( + 401, + "Your secret key could not be parsed. Secret keys start with sk_ and should be passed in the authorization header as Bearer sk_...", + ); + } + + if (type === "public" && !split[1].startsWith("pk_")) { + throw new HttpException( + 401, + "Your public key could not be parsed. Public keys start with pk_ and should be passed in the authorization header as Bearer sk_...", + ); + } + + return split[1]; +} diff --git a/packages/api/src/services/ActionService.ts b/packages/api/src/services/ActionService.ts new file mode 100644 index 0000000..41fef46 --- /dev/null +++ b/packages/api/src/services/ActionService.ts @@ -0,0 +1,185 @@ +import type { Contact, Event, Project } from "@prisma/client"; +import dayjs from "dayjs"; +import { prisma } from "../database/prisma"; +import { ContactService } from "./ContactService"; +import { EmailService } from "./EmailService"; +import { Keys } from "./keys"; +import { wrapRedis } from "./redis"; + +export class ActionService { + /** + * Gets an action by its ID + * @param id + */ + public static id(id: string) { + return wrapRedis(Keys.Action.id(id), async () => { + return prisma.action.findUnique({ + where: { id }, + include: { + events: true, + notevents: true, + triggers: true, + emails: true, + template: true, + }, + }); + }); + } + + /** + * Gets all actions that share an event with the action with the given ID + * @param id + */ + public static related(id: string) { + return wrapRedis(Keys.Action.related(id), async () => { + const action = await ActionService.id(id); + + if (!action) { + return []; + } + + return prisma.action.findMany({ + where: { + events: { some: { id: { in: action.events.map((e) => e.id) } } }, + id: { not: action.id }, + }, + include: { events: true }, + }); + }); + } + + /** + * Gets all actions that have an event as a trigger + * @param eventId + */ + public static event(eventId: string) { + return wrapRedis(Keys.Action.event(eventId), async () => { + return prisma.event + .findUniqueOrThrow({ where: { id: eventId } }) + .actions({ + include: { events: true, template: true, notevents: true }, + }); + }); + } + + /** + * Takes a contact and an event and triggers all required actions + * @param contact + * @param event + * @param project + */ + public static async trigger({ + event, + contact, + project, + }: { event: Event; contact: Contact; project: Project }) { + const actions = await ActionService.event(event.id); + + const triggers = await ContactService.triggers(contact.id); + + for (const action of actions) { + const hasTriggeredAction = !!triggers.find( + (t) => t.actionId === action.id, + ); + + if (action.runOnce && hasTriggeredAction) { + // User has already triggered this run once action + continue; + } + + if ( + action.notevents.length > 0 && + action.notevents.some((e) => triggers.some((t) => t.eventId === e.id)) + ) { + continue; + } + + let triggeredEvents = triggers.filter((t) => t.eventId === event.id); + + if (hasTriggeredAction) { + const lastActionTrigger = triggers.filter( + (t) => t.contactId === contact.id && t.actionId === action.id, + )[0]; + + triggeredEvents = triggeredEvents.filter( + (e) => e.createdAt > lastActionTrigger.createdAt, + ); + } + + const updatedTriggers = [ + ...new Set(triggeredEvents.map((t) => t.eventId)), + ]; + const requiredTriggers = action.events.map((e) => e.id); + + if ( + updatedTriggers.sort().join(",") !== requiredTriggers.sort().join(",") + ) { + // Not all required events have been triggered + continue; + } + + await prisma.trigger.create({ + data: { actionId: action.id, contactId: contact.id }, + }); + + if (!contact.subscribed && action.template.type === "MARKETING") { + continue; + } + + if (action.delay === 0) { + const { subject, body } = EmailService.format({ + subject: action.template.subject, + body: action.template.body, + data: { + plunk_id: contact.id, + plunk_email: contact.email, + ...JSON.parse(contact.data ?? "{}"), + }, + }); + + const { messageId } = await EmailService.send({ + from: { + name: project.from ?? project.name, + email: + project.verified && project.email + ? project.email + : "no-reply@useplunk.dev", + }, + to: [contact.email], + content: { + subject, + html: EmailService.compile({ + content: body, + footer: { + unsubscribe: action.template.type === "MARKETING", + }, + contact: { + id: contact.id, + }, + project: { + name: project.name, + }, + isHtml: action.template.style === "HTML", + }), + }, + }); + + await prisma.email.create({ + data: { + messageId, + actionId: action.id, + contactId: contact.id, + }, + }); + } else { + await prisma.task.create({ + data: { + actionId: action.id, + contactId: contact.id, + runBy: dayjs().add(action.delay, "minutes").toDate(), + }, + }); + } + } + } +} diff --git a/packages/api/src/services/AuthService.ts b/packages/api/src/services/AuthService.ts new file mode 100644 index 0000000..6be1918 --- /dev/null +++ b/packages/api/src/services/AuthService.ts @@ -0,0 +1,18 @@ +import {prisma} from '../database/prisma'; +import {verifyHash} from '../util/hash'; + +export class AuthService { + public static async verifyCredentials(email: string, password: string) { + const user = await prisma.user.findUnique({ + where: { + email: email, + }, + }); + + if (!user?.password) { + return false; + } + + return await verifyHash(password, user.password); + } +} diff --git a/packages/api/src/services/CampaignService.ts b/packages/api/src/services/CampaignService.ts new file mode 100644 index 0000000..2ea40b2 --- /dev/null +++ b/packages/api/src/services/CampaignService.ts @@ -0,0 +1,17 @@ +import {Keys} from './keys'; +import {wrapRedis} from './redis'; +import {prisma} from '../database/prisma'; + +export class CampaignService { + public static id(id: string) { + return wrapRedis(Keys.Campaign.id(id), async () => { + return prisma.campaign.findUnique({ + where: {id}, + include: { + recipients: {select: {id: true}}, + emails: {select: {id: true, status: true, contact: {select: {id: true, email: true}}}}, + }, + }); + }); + } +} diff --git a/packages/api/src/services/ContactService.ts b/packages/api/src/services/ContactService.ts new file mode 100644 index 0000000..ccc1140 --- /dev/null +++ b/packages/api/src/services/ContactService.ts @@ -0,0 +1,24 @@ +import {Keys} from './keys'; +import {wrapRedis} from './redis'; +import {prisma} from '../database/prisma'; + +export class ContactService { + public static id(id: string) { + return wrapRedis(Keys.Contact.id(id), async () => { + return prisma.contact.findUnique({ + where: {id}, + include: {triggers: {include: {event: true, action: true}}, emails: {where: {subject: {not: null}}}}, + }); + }); + } + + public static email(projectId: string, email: string) { + return wrapRedis(Keys.Contact.email(projectId, email), () => { + return prisma.contact.findFirst({where: {projectId, email}}); + }); + } + + public static async triggers(id: string) { + return prisma.contact.findUniqueOrThrow({where: {id}}).triggers(); + } +} diff --git a/packages/api/src/services/EmailService.ts b/packages/api/src/services/EmailService.ts new file mode 100644 index 0000000..ff140c2 --- /dev/null +++ b/packages/api/src/services/EmailService.ts @@ -0,0 +1,531 @@ +import mjml2html from "mjml"; +import { APP_URI, AWS_SES_CONFIGURATION_SET } from "../app/constants"; +import { ses } from "../util/ses"; + +export class EmailService { + public static async send({ + from, + to, + content, + reply, + headers, + }: { + from: { + name: string; + email: string; + }; + reply?: string; + to: string[]; + content: { + subject: string; + html: string; + }; + headers?: { + [key: string]: string; + } | null; + }) { + // Check if the body contains an unsubscribe link + const regex = /unsubscribe\/([a-f\d-]+)"/; + const containsUnsubscribeLink = content.html.match(regex); + + let unsubscribeLink = ""; + if (containsUnsubscribeLink?.[1]) { + const unsubscribeId = containsUnsubscribeLink[1]; + unsubscribeLink = `List-Unsubscribe: `; + } + + const rawMessage = `From: ${from.name} <${from.email}> +To: ${to.join(", ")} +Reply-To: ${reply || from.email} +Subject: ${content.subject} +MIME-Version: 1.0 +Content-Type: multipart/alternative; boundary="NextPart" +${ + headers + ? Object.entries(headers) + .map(([key, value]) => `${key}: ${value}`) + .join("\n") + : "" +} +${unsubscribeLink} + +--NextPart +Content-Type: text/html; charset=utf-8 +Content-Transfer-Encoding: 7bit + +${EmailService.breakLongLines(content.html, 500)} +--NextPart-- +`; + + const response = await ses.sendRawEmail({ + Destinations: to, + ConfigurationSetName: AWS_SES_CONFIGURATION_SET, + RawMessage: { + Data: new TextEncoder().encode(rawMessage), + }, + Source: `${from.name} <${from.email}>`, + }); + + if (!response.MessageId) { + throw new Error("Could not send email"); + } + + return { messageId: response.MessageId }; + } + + public static compile({ + content, + footer, + contact, + project, + isHtml, + }: { + content: string; + + project: { + name: string; + }; + contact: { + id: string; + }; + footer: { + unsubscribe?: boolean; + }; + isHtml?: boolean; + }) { + const html = content.replace(/ + + + +
+

+ You received this email because you agreed to receive emails from ${project.name}. If you no longer wish to receive emails like this, please + update your preferences. +

+ + + + ` + : "" +}`; + } + return mjml2html( + ` + + + + .prose { + color: #4a5568; + max-width: 600px; + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol'; + } + + .prose [class~="lead"] { + color: #4a5568; + font-size: 20px; + line-height: 32px; + margin-top: 19px; + margin-bottom: 19px; + } + + .prose a { + color: #1a202c; + text-decoration: underline; + } + + .prose strong { + color: #1a202c; + font-weight: 600; + } + + .prose ol { + counter-reset: list-counter; + margin-top: 20px; + margin-bottom: 20px; + } + + .prose ol > li { + position: relative; + counter-increment: list-counter; + padding-left: 28px; + } + + .prose ol > li::before { + content: counter(list-counter) "."; + position: absolute; + font-weight: 400; + color: #718096; + } + + .prose ul > li { + position: relative; + padding-left: 28px; + } + + .prose ul > li::before { + content: ""; + position: absolute; + background-color: #cbd5e0; + border-radius: 50%; + width: 6px; + height: 6px; + top: 11px; + left: 4px; + } + + .prose hr { + border-color: #e2e8f0; + border-top-width: 1px; + margin-top: 42px; + margin-bottom: 42px; + } + + .prose blockquote { + font-weight: 500; + font-style: italic; + color: #1a202c; + border-left: 4px solid #e2e8f0; + quotes: initial; + margin-top: 25px; + margin-bottom: 25px; + padding-left: 16px; + } + + .prose h1 { + color: #1a202c; + font-weight: 800; + font-size: 36px; + margin-top: 0px; + margin-bottom: 14px; + line-height: 40px; + } + + .prose h2 { + color: #1a202c; + font-weight: 700; + font-size: 24px; + margin-top: 32px; + margin-bottom: 16px; + line-height: 32px; + } + + .prose h3 { + color: #1a202c; + font-weight: 600; + font-size: 20px; + margin-top: 25px; + margin-bottom: 9.6px; + line-height: 32px; + } + + .prose h4 { + color: #1a202c; + font-weight: 600; + margin-top: 24px; + margin-bottom: 8px; + line-height: 1.5; + } + + .prose figure figcaption { + color: #718096; + font-size: 14px; + line-height: 1.4; + margin-top: 14px; + } + + .prose code { + color: #1a202c; + font-weight: 600; + font-size: 14px; + } + + .prose code::before { + content: "\`"; + } + + .prose code::after { + content: "\`"; + } + + .prose pre { + color: #e2e8f0; + background-color: #2d3748; + overflow-x: auto; + font-size: 14px; + line-height: 1.7142857; + margin-top: 27px; + margin-bottom: 27px; + border-radius: 6px; + padding-top: 13px; + padding-right: 18px; + padding-bottom: 13px; + padding-left: 18px; + } + + .prose pre code { + background-color: transparent; + border-width: 0; + border-radius: 0; + padding: 0; + font-weight: 400; + color: inherit; + font-size: inherit; + font-family: inherit; + line-height: inherit; + } + + .prose pre code::before { + content: ""; + } + + .prose pre code::after { + content: ""; + } + + .prose table { + width: 100%; + table-layout: auto; + margin-top: 32px; + margin-bottom: 32px; + font-size: 11px; + line-height: 1.7142857; + } + + .prose thead { + color: #1a202c; + font-weight: 600; + border-bottom: 1px solid #cbd5e0; + } + + .prose thead th { + vertical-align: bottom; + padding-right: 9px; + padding-bottom: 9px; + padding-left: 9px; + } + + .prose tbody tr { + border-bottom: 1px solid #e2e8f0; + } + + .prose tbody tr:last-child { + border-bottom-width: 0; + } + + .prose tbody td { + vertical-align: top; + padding-top: 9px; + padding-right: 9px; + padding-bottom: 9px; + padding-left: 9px; + } + + .prose { + font-size: 16px; + line-height: 1.75; + } + + .prose p { + margin-top: 20px; + margin-bottom: 20px; + } + + .prose img { + margin-top: 32px; + margin-bottom: 32px; + max-width: 100%; + height: auto; + display: block; + } + + .prose video { + margin-top: 32px; + margin-bottom: 32px; + } + + .prose figure { + margin-top: 32px; + margin-bottom: 32px; + } + + .prose figure > * { + margin-top: 0; + margin-bottom: 0; + } + + .prose h2 code { + font-size: 14px; + } + + .prose h3 code { + font-size: 14px; + } + + .prose ul { + margin-top: 20px; + margin-bottom: 20px; + } + + .prose li { + margin-top: 8px; + margin-bottom: 8px; + } + + .prose ol > li:before { + left: 0; + } + + .prose > ul > li p { + margin-top: 12px; + margin-bottom: 12px; + } + + .prose > ul > li > *:first-child { + margin-top: 20px; + } + + .prose > ul > li > *:last-child { + margin-bottom: 20px; + } + + .prose > ol > li > *:first-child { + margin-top: 20px; + } + + .prose > ol > li > *:last-child { + margin-bottom: 20px; + } + + .prose ul ul, + .prose ul ol, + .prose ol ul, + .prose ol ol { + margin-top: 12px; + margin-bottom: 12px; + } + + .prose hr + * { + margin-top: 0; + } + + .prose h2 + * { + margin-top: 0; + } + + .prose h3 + * { + margin-top: 0; + } + + .prose h4 + * { + margin-top: 0; + } + + .prose thead th:first-child { + padding-left: 0; + } + + .prose thead th:last-child { + padding-right: 0; + } + + .prose tbody td:first-child { + padding-left: 0; + } + + .prose tbody td:last-child { + padding-right: 0; + } + + .prose > :first-child { + margin-top: 0; + } + + .prose > :last-child { + margin-bottom: 0; + } + + + + + + + + + ${html} + + + + + + + + ${ + footer.unsubscribe + ? ` + + +

+ You received this email because you agreed to receive emails from ${project.name}. If you no longer wish to receive emails like this, please update your preferences. +

+
+ ` + : "" + } +
+
+
+
`, + ).html.replace(/^\s+|\s+$/g, ""); + } + + public static format({ + subject, + body, + data, + }: { subject: string; body: string; data: Record }) { + return { + subject: subject.replace(/\{\{(.*?)}}/g, (match, key) => { + const [mainKey, defaultValue] = key + .split("??") + .map((s: string) => s.trim()); + return data[mainKey] ?? defaultValue ?? ""; + }), + body: body.replace(/\{\{(.*?)}}/g, (match, key) => { + const [mainKey, defaultValue] = key + .split("??") + .map((s: string) => s.trim()); + if (Array.isArray(data[mainKey])) { + return data[mainKey].map((e: string) => `
  • ${e}
  • `).join("\n"); + } + return data[mainKey] ?? defaultValue ?? ""; + }), + }; + } + + private static breakLongLines(input: string, maxLineLength: number): string { + const lines = input.split("\n"); + const result = []; + for (let line of lines) { + while (line.length > maxLineLength) { + let pos = maxLineLength; + while (pos > 0 && line[pos] !== " ") { + pos--; + } + if (pos === 0) { + pos = maxLineLength; + } + result.push(line.substring(0, pos)); + line = line.substring(pos).trim(); + } + result.push(line); + } + return result.join("\n"); + } +} diff --git a/packages/api/src/services/EventService.ts b/packages/api/src/services/EventService.ts new file mode 100644 index 0000000..c605852 --- /dev/null +++ b/packages/api/src/services/EventService.ts @@ -0,0 +1,25 @@ +import {prisma} from '../database/prisma'; +import {REDIS_ONE_MINUTE, wrapRedis} from './redis'; +import {Keys} from './keys'; + +export class EventService { + public static id(id: string) { + return wrapRedis( + Keys.Event.id(id), + () => { + return prisma.event.findUnique({where: {id}}); + }, + REDIS_ONE_MINUTE * 1440, + ); + } + + public static event(projectId: string, name: string) { + return wrapRedis( + Keys.Event.event(projectId, name), + () => { + return prisma.event.findFirst({where: {projectId, name}}); + }, + REDIS_ONE_MINUTE * 1440, + ); + } +} diff --git a/packages/api/src/services/MembershipService.ts b/packages/api/src/services/MembershipService.ts new file mode 100644 index 0000000..46b380b --- /dev/null +++ b/packages/api/src/services/MembershipService.ts @@ -0,0 +1,76 @@ +import type { Role } from "@prisma/client"; +import { NODE_ENV } from "../app/constants"; +import { prisma } from "../database/prisma"; +import { Keys } from "./keys"; +import { redis, wrapRedis } from "./redis"; + +export class MembershipService { + public static async isMember(projectId: string, userId: string) { + return wrapRedis( + Keys.ProjectMembership.isMember(projectId, userId), + async () => { + if (NODE_ENV === "development") { + return true; + } + + const membership = await prisma.projectMembership.findFirst({ + where: { projectId, userId }, + }); + + return !!membership; + }, + ); + } + + public static async isAdmin(projectId: string, userId: string) { + return wrapRedis( + Keys.ProjectMembership.isAdmin(projectId, userId), + async () => { + if (NODE_ENV === "development") { + return true; + } + + const membership = await prisma.projectMembership.findFirst({ + where: { projectId, userId, role: { in: ["ADMIN", "OWNER"] } }, + }); + + return !!membership; + }, + ); + } + + public static async isOwner(projectId: string, userId: string) { + return wrapRedis( + Keys.ProjectMembership.isOwner(projectId, userId), + async () => { + if (NODE_ENV === "development") { + return true; + } + + const membership = await prisma.projectMembership.findFirst({ + where: { projectId, userId, role: "OWNER" }, + }); + + return !!membership; + }, + ); + } + + public static async kick(projectId: string, userId: string) { + await prisma.projectMembership.delete({ + where: { userId_projectId: { projectId, userId } }, + }); + + await redis.del(Keys.Project.memberships(projectId)); + await redis.del(Keys.User.projects(userId)); + } + + public static async invite(projectId: string, userId: string, role: Role) { + await prisma.projectMembership.create({ + data: { projectId, userId, role }, + }); + + await redis.del(Keys.Project.memberships(projectId)); + await redis.del(Keys.User.projects(userId)); + } +} diff --git a/packages/api/src/services/ProjectService.ts b/packages/api/src/services/ProjectService.ts new file mode 100644 index 0000000..b7e4497 --- /dev/null +++ b/packages/api/src/services/ProjectService.ts @@ -0,0 +1,468 @@ +import dayjs from "dayjs"; +import { prisma } from "../database/prisma"; +import { Keys } from "./keys"; +import { wrapRedis } from "./redis"; + +export class ProjectService { + public static contacts = { + get: (id: string) => { + return wrapRedis(Keys.Project.contacts(id), async () => { + return prisma.project.findUnique({ where: { id } }).contacts({ + select: { + id: true, + email: true, + subscribed: true, + createdAt: true, + data: true, + updatedAt: true, + triggers: { select: { createdAt: true, eventId: true } }, + }, + }); + }); + }, + + paginated: (id: string, page: number) => { + return wrapRedis(Keys.Project.contacts(id, { page }), async () => { + return prisma.project.findUnique({ where: { id } }).contacts({ + select: { + id: true, + email: true, + subscribed: true, + createdAt: true, + triggers: { select: { createdAt: true } }, + emails: { select: { createdAt: true } }, + }, + orderBy: [{ createdAt: "desc" }], + take: 20, + skip: (page - 1) * 20, + }); + }); + }, + + count: (id: string) => { + return wrapRedis(Keys.Project.contacts(id, { count: true }), async () => { + return prisma.contact.count({ where: { projectId: id } }); + }); + }, + }; + + public static emails = { + get: (id: string) => { + return wrapRedis(Keys.Project.emails(id), async () => { + return prisma.email.findMany({ + where: { + OR: [ + { action: { projectId: id } }, + { campaign: { projectId: id } }, + { projectId: id }, + ], + }, + orderBy: { createdAt: "desc" }, + }); + }); + }, + count: (id: string) => { + return wrapRedis(Keys.Project.emails(id, { count: true }), async () => { + return prisma.email.count({ where: { contact: { projectId: id } } }); + }); + }, + }; + + public static id(id: string) { + return wrapRedis(Keys.Project.id(id), async () => { + return prisma.project.findUnique({ where: { id } }); + }); + } + + public static key(key: string) { + if (key.startsWith("sk_")) { + return ProjectService.secret(key); + } + return ProjectService.public(key); + } + + public static secret(secretKey: string) { + return wrapRedis(Keys.Project.secret(secretKey), () => { + return prisma.project.findUnique({ where: { secret: secretKey } }); + }); + } + + public static public(publicKey: string) { + return wrapRedis(Keys.Project.public(publicKey), () => { + return prisma.project.findUnique({ where: { public: publicKey } }); + }); + } + + public static async secretIsAvailable(secretKey: string) { + const project = await ProjectService.secret(secretKey); + + return !project; + } + + public static async publicIsAvailable(publicKey: string) { + const project = await ProjectService.public(publicKey); + + return !project; + } + + public static memberships(id: string) { + return wrapRedis(Keys.Project.memberships(id), async () => { + const memberships = await prisma.project + .findUnique({ where: { id } }) + .memberships({ include: { user: true } }); + + if (!memberships) { + return []; + } + + return memberships.map((membership) => { + return { + userId: membership.userId, + email: membership.user.email, + role: membership.role, + }; + }); + }); + } + + public static metadata(id: string) { + return wrapRedis(Keys.Project.metadata(id), async () => { + const contacts = await prisma.project + .findUnique({ where: { id } }) + .contacts({ + where: { + data: { + not: null, + }, + }, + distinct: ["data"], + select: { + data: true, + }, + }); + + if (!contacts) { + return []; + } + + return [ + ...new Set( + contacts + .filter((c) => c.data) + .flatMap((c) => Object.keys(JSON.parse(c.data as string))), + ), + ]; + }); + } + + public static async feed(id: string, page: number) { + const itemsPerPage = 10; + const skip = (page - 1) * itemsPerPage; + + const triggers = await prisma.trigger.findMany({ + where: { contact: { projectId: id } }, + include: { + contact: { + select: { + id: true, + email: true, + }, + }, + event: { + select: { + name: true, + }, + }, + }, + orderBy: { createdAt: "desc" }, + }); + + const emails = await prisma.email.findMany({ + where: { contact: { projectId: id } }, + include: { + contact: { + select: { + id: true, + email: true, + }, + }, + }, + orderBy: { createdAt: "desc" }, + }); + + const combined = [...triggers, ...emails]; + combined.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime()); + + return combined.slice(skip, skip + itemsPerPage); + } + + public static usage(id: string) { + return wrapRedis(Keys.Project.usage(id), async () => { + const transactional = await prisma.email.count({ + where: { + projectId: id, + createdAt: { + gte: new Date(dayjs().startOf("month").toISOString()), + lte: new Date(dayjs().endOf("month").toISOString()), + }, + }, + }); + + const automation = await prisma.email.count({ + where: { + action: { projectId: id }, + createdAt: { + gte: new Date(dayjs().startOf("month").toISOString()), + lte: new Date(dayjs().endOf("month").toISOString()), + }, + }, + }); + + const campaign = await prisma.email.count({ + where: { + campaign: { projectId: id }, + createdAt: { + gte: new Date(dayjs().startOf("month").toISOString()), + lte: new Date(dayjs().endOf("month").toISOString()), + }, + }, + }); + + return { + transactional, + automation, + campaign, + }; + }); + } + + public static events(id: string, triggers: boolean) { + return wrapRedis(Keys.Project.events(id, triggers), async () => { + if (triggers) { + return prisma.project.findUnique({ where: { id } }).events({ + include: { + triggers: { + select: { id: true, createdAt: true, contactId: true }, + }, + }, + orderBy: { createdAt: "desc" }, + }); + } + return prisma.project.findUnique({ where: { id } }).events({ + orderBy: { createdAt: "desc" }, + }); + }); + } + + public static actions(id: string) { + return wrapRedis(Keys.Project.actions(id), async () => { + return prisma.project.findUnique({ where: { id } }).actions({ + include: { + triggers: { select: { id: true } }, + template: true, + emails: { select: { id: true, status: true } }, + tasks: { select: { id: true } }, + }, + }); + }); + } + + public static templates(id: string) { + return wrapRedis(Keys.Project.templates(id), async () => { + return prisma.project.findUnique({ where: { id } }).templates({ + include: { actions: true }, + orderBy: { createdAt: "desc" }, + }); + }); + } + + public static campaigns(id: string) { + return wrapRedis(Keys.Project.campaigns(id), async () => { + return prisma.project.findUnique({ where: { id } }).campaigns({ + include: { + recipients: { select: { id: true } }, + emails: { select: { id: true, status: true } }, + tasks: { select: { id: true } }, + }, + orderBy: { createdAt: "desc" }, + }); + }); + } + + public static analytics(params: { + id: string; + method?: "week" | "month" | "year"; + }) { + return wrapRedis(Keys.Project.analytics(params.id), async () => { + const methods = { + week: { + daysBack: 7, + method: "week", + }, + month: { + daysBack: 30, + method: "day", + }, + year: { + daysBack: 365, + method: "month", + }, + }; + + const end = dayjs().toDate(); + const start = dayjs() + .subtract(methods[params.method ?? "week"].daysBack, "days") + .toDate(); + + const contacts = await prisma.$queryRaw` +WITH date_range AS ( + SELECT generate_series( + (SELECT DATE_TRUNC('day', MIN("createdAt")) FROM contacts), + DATE_TRUNC('day', NOW()) + INTERVAL '1 day', + INTERVAL '1 day' + ) AS day +) + +SELECT + dr.day, + SUM(COALESCE(ct.count, 0)) OVER (ORDER BY dr.day) as count +FROM date_range dr +LEFT JOIN ( + SELECT + DATE_TRUNC('day', c."createdAt") AS day, + COUNT(c.id) as count + FROM contacts c + WHERE "projectId" = ${params.id} + GROUP BY DATE_TRUNC('day', c."createdAt") +) ct ON dr.day = ct.day +WHERE dr.day < DATE_TRUNC('day', NOW()) +ORDER BY dr.day DESC +LIMIT 30; + + `; + + const rawActionClicks = await prisma.$queryRaw` +SELECT clicks."link", a."name", count(clicks.id)::int FROM clicks +JOIN emails e on clicks."emailId" = e.id +JOIN actions a on e."actionId" = a.id +WHERE clicks."link" NOT LIKE '%unsubscribe%' AND DATE(clicks."createdAt") BETWEEN DATE(${start}) AND DATE(${end}) AND a."projectId" = ${params.id} +GROUP BY a."name", clicks."link" + `; + + const combinedRoutes = {}; + + // @ts-expect-error + rawActionClicks.forEach((item) => { + const url = new URL(item.link); + const route = url.pathname; + // @ts-expect-error + if (combinedRoutes[route]) { + // @ts-expect-error + combinedRoutes[route].count += item.count; + } else { + // @ts-expect-error + combinedRoutes[route] = { + link: url.hostname + route, + name: item.name, + count: item.count, + }; + } + }); + + const formattedActionClicks = Object.values(combinedRoutes).sort( + // @ts-expect-error + (a, b) => b.count - a.count, + ); + + const subscribed = await prisma.contact.count({ + where: { subscribed: true, projectId: params.id }, + }); + const unsubscribed = await prisma.contact.count({ + where: { subscribed: false, projectId: params.id }, + }); + + const opened = await prisma.email.count({ + where: { + contact: { projectId: params.id }, + status: "OPENED", + }, + }); + + const openedPrev = await prisma.email.count({ + where: { + contact: { projectId: params.id }, + status: "OPENED", + createdAt: { + lte: start, + }, + }, + }); + + const bounced = await prisma.email.count({ + where: { + contact: { projectId: params.id }, + status: "BOUNCED", + }, + }); + + const bouncedPrev = await prisma.email.count({ + where: { + contact: { projectId: params.id }, + status: "BOUNCED", + createdAt: { + lte: start, + }, + }, + }); + + const complaint = await prisma.email.count({ + where: { + contact: { projectId: params.id }, + status: "COMPLAINT", + }, + }); + + const complaintPrev = await prisma.email.count({ + where: { + contact: { projectId: params.id }, + status: "COMPLAINT", + createdAt: { + lte: start, + }, + }, + }); + + const total = await prisma.email.count({ + where: { + contact: { projectId: params.id }, + }, + }); + + const totalPrev = await prisma.email.count({ + where: { + contact: { projectId: params.id }, + createdAt: { + lte: start, + }, + }, + }); + + return { + contacts: { timeseries: contacts, subscribed, unsubscribed }, + emails: { + total, + opened, + bounced, + complaint, + totalPrev, + bouncedPrev, + complaintPrev, + openedPrev, + }, + clicks: { + actions: formattedActionClicks, + }, + }; + }); + } +} diff --git a/packages/api/src/services/TemplateService.ts b/packages/api/src/services/TemplateService.ts new file mode 100644 index 0000000..fc4b5af --- /dev/null +++ b/packages/api/src/services/TemplateService.ts @@ -0,0 +1,17 @@ +import {wrapRedis} from './redis'; +import {prisma} from '../database/prisma'; +import {Keys} from './keys'; + +export class TemplateService { + public static id(id: string) { + return wrapRedis(Keys.Template.id(id), async () => { + return prisma.template.findUnique({where: {id}, include: {actions: true}}); + }); + } + + public static actions(templateId: string) { + return wrapRedis(Keys.Template.actions(templateId), async () => { + return prisma.template.findUnique({where: {id: templateId}}).actions(); + }); + } +} diff --git a/packages/api/src/services/UserService.ts b/packages/api/src/services/UserService.ts new file mode 100644 index 0000000..d976239 --- /dev/null +++ b/packages/api/src/services/UserService.ts @@ -0,0 +1,61 @@ +import dayjs from "dayjs"; +import { NODE_ENV } from "../app/constants"; +import { prisma } from "../database/prisma"; +import { Keys } from "./keys"; +import { wrapRedis } from "./redis"; + +export class UserService { + public static readonly COOKIE_NAME = "token"; + + public static id(id: string) { + return wrapRedis(Keys.User.id(id), () => { + return prisma.user.findUnique({ + where: { id }, + }); + }); + } + + public static email(email: string) { + return wrapRedis(Keys.User.email(email), () => { + return prisma.user.findUnique({ + where: { email }, + }); + }); + } + + public static async projects(id: string) { + return wrapRedis(Keys.User.projects(id), async () => { + const user = await prisma.user.findUnique({ + where: { id }, + include: { memberships: true }, + }); + + if (!user) { + return []; + } + + return prisma.project.findMany({ + where: { + id: { + in: user.memberships.map((project) => project.projectId), + }, + }, + orderBy: { name: "asc" }, + }); + }); + } + + /** + * Generates cookie options + * @param expires An optional expiry for this cookie (useful for a logout) + */ + public static cookieOptions(expires?: Date) { + return { + httpOnly: true, + expires: expires ?? dayjs().add(168, "hours").toDate(), + secure: NODE_ENV !== "development", + sameSite: "lax", + path: "/", + } as const; + } +} diff --git a/packages/api/src/services/keys.ts b/packages/api/src/services/keys.ts new file mode 100644 index 0000000..7f2748d --- /dev/null +++ b/packages/api/src/services/keys.ts @@ -0,0 +1,140 @@ +export const Keys = { + User: { + id(id: string): string { + return `account:id:${id}`; + }, + email(email: string): string { + return `account:${email}`; + }, + projects(id: string): string { + return `account:${id}:projects`; + }, + }, + Project: { + id(id: string): string { + return `project:id:${id}`; + }, + secret(secretKey: string): string { + return `project:secret:${secretKey}`; + }, + public(publicKey: string): string { + return `project:public:${publicKey}`; + }, + memberships(id: string): string { + return `project:${id}:memberships`; + }, + usage(id: string): string { + return `project:${id}:usage`; + }, + events(id: string, triggers: boolean): string { + if (triggers) { + return `project:${id}:events:triggers`; + } + + return `project:${id}:events`; + }, + metadata(id: string): string { + return `project:${id}:metadata`; + }, + actions(id: string): string { + return `project:${id}:actions`; + }, + templates(id: string): string { + return `project:${id}:templates`; + }, + feed(id: string): string { + return `project:${id}:feed`; + }, + contacts( + id: string, + options?: { + page?: number; + count?: boolean; + }, + ): string { + if (options?.count) { + return `project:${id}:contacts:count`; + } + + if (options?.page) { + return `project:${id}:contacts:page:${options.page}`; + } + + return `project:${id}:contacts`; + }, + campaigns(id: string): string { + return `project:${id}:campaigns`; + }, + analytics(id: string): string { + return `project:${id}:analytics`; + }, + emails( + id: string, + options?: { + count?: boolean; + }, + ): string { + if (options?.count) { + return `project:${id}:emails:count`; + } + + return `project:${id}:emails`; + }, + }, + ProjectMembership: { + isMember(projectId: string, accountId: string) { + return `project:id:${projectId}:ismember:${accountId}`; + }, + isAdmin(projectId: string, accountId: string) { + return `project:id:${projectId}:isadmin:${accountId}`; + }, + isOwner(projectId: string, accountId: string) { + return `project:id:${projectId}:isowner:${accountId}`; + }, + }, + Campaign: { + id(id: string): string { + return `campaign:id:${id}`; + }, + }, + Template: { + id(id: string): string { + return `template:id:${id}`; + }, + actions(templateId: string): string { + return `template:id:${templateId}:actions`; + }, + }, + Webhook: { + id(id: string): string { + return `webhook:id:${id}`; + }, + }, + Contact: { + id(id: string): string { + return `contact:id:${id}`; + }, + email(projectId: string, email: string): string { + return `project:id:${projectId}:contact:email:${email}`; + }, + }, + Action: { + id(id: string): string { + return `action:id:${id}`; + }, + related(id: string): string { + return `action:id:${id}:related`; + }, + event(eventId: string): string { + return `action:event:id:${eventId}`; + }, + }, + Event: { + id(id: string): string { + return `event:id:${id}`; + }, + event(projectId: string, name: string): string { + return `project:id:${projectId}:event:name:${name}`; + }, + }, +}; diff --git a/packages/api/src/services/redis.ts b/packages/api/src/services/redis.ts new file mode 100644 index 0000000..64879ed --- /dev/null +++ b/packages/api/src/services/redis.ts @@ -0,0 +1,27 @@ +import Redis from 'ioredis'; +import {REDIS_URL} from '../app/constants'; + +export const redis = new Redis(REDIS_URL); + +export const REDIS_ONE_MINUTE = 60; +export const REDIS_DEFAULT_EXPIRY = REDIS_ONE_MINUTE / 60; + +/** + * @param key The key for redis (use Keys#) + * @param fn The function to return a resource. Can be a promise + * @param seconds The amount of seconds to hold this resource in redis for. Defaults to 60 + */ +export async function wrapRedis(key: string, fn: () => Promise, seconds = REDIS_DEFAULT_EXPIRY): Promise { + const cached = await redis.get(key); + if (cached) { + return JSON.parse(cached); + } + + const recent = await fn(); + + if (recent) { + await redis.set(key, JSON.stringify(recent), 'EX', seconds); + } + + return recent; +} diff --git a/packages/api/src/util/hash.ts b/packages/api/src/util/hash.ts new file mode 100644 index 0000000..12eee6c --- /dev/null +++ b/packages/api/src/util/hash.ts @@ -0,0 +1,33 @@ +import bcrypt from "bcrypt"; + +/** + * Verifies a hash against a password + * @param {string} pass The password + * @param {string} hash The hash + */ +export const verifyHash = (pass: string, hash: string) => { + return new Promise((resolve, reject) => { + void bcrypt.compare(pass, hash, (err, res) => { + if (err) { + return reject(err); + } + return resolve(res); + }); + }); +}; + +/** + * Generates a hash from plain text + * @param {string} pass The password + * @returns {Promise} Password hash + */ +export const createHash = (pass: string): Promise => { + return new Promise((resolve, reject) => { + void bcrypt.hash(pass, 10, (err, res) => { + if (err) { + return reject(err); + } + resolve(res); + }); + }); +}; diff --git a/packages/api/src/util/ses.ts b/packages/api/src/util/ses.ts new file mode 100644 index 0000000..b56b692 --- /dev/null +++ b/packages/api/src/util/ses.ts @@ -0,0 +1,53 @@ +import { SES } from "@aws-sdk/client-ses"; +import { + AWS_ACCESS_KEY_ID, + AWS_REGION, + AWS_SECRET_ACCESS_KEY, +} from "../app/constants"; + +export const ses = new SES({ + apiVersion: "2010-12-01", + region: AWS_REGION, + credentials: { + accessKeyId: AWS_ACCESS_KEY_ID, + secretAccessKey: AWS_SECRET_ACCESS_KEY, + }, +}); + +export const getIdentities = async (identities: string[]) => { + const res = await ses.getIdentityVerificationAttributes({ + Identities: identities.flatMap((identity) => [identity.split("@")[1]]), + }); + + const parsedResult = Object.entries(res.VerificationAttributes ?? {}); + return parsedResult.map((obj) => { + return { email: obj[0], status: obj[1].VerificationStatus }; + }); +}; + +export const verifyIdentity = async (email: string) => { + const DKIM = await ses.verifyDomainDkim({ + Domain: email.includes("@") ? email.split("@")[1] : email, + }); + + await ses.setIdentityMailFromDomain({ + Identity: email.includes("@") ? email.split("@")[1] : email, + MailFromDomain: `plunk.${email.includes("@") ? email.split("@")[1] : email}`, + }); + + return DKIM.DkimTokens; +}; + +export const getIdentityVerificationAttributes = async (email: string) => { + const attributes = await ses.getIdentityDkimAttributes({ + Identities: [email, email.split("@")[1]], + }); + + const parsedAttributes = Object.entries(attributes.DkimAttributes ?? {}); + + return { + email: parsedAttributes[0][0], + tokens: parsedAttributes[0][1].DkimTokens, + status: parsedAttributes[0][1].DkimVerificationStatus, + }; +}; diff --git a/packages/api/src/util/tokens.ts b/packages/api/src/util/tokens.ts new file mode 100644 index 0000000..0467884 --- /dev/null +++ b/packages/api/src/util/tokens.ts @@ -0,0 +1,9 @@ +import { randomBytes } from "node:crypto"; + +/** + * A function that generates a random 24 byte API secret + * @param type + */ +export function generateToken(type: "secret" | "public") { + return `${type === "secret" ? "sk" : "pk"}_${randomBytes(24).toString("hex")}`; +} diff --git a/packages/api/tsconfig.json b/packages/api/tsconfig.json new file mode 100644 index 0000000..c2d619e --- /dev/null +++ b/packages/api/tsconfig.json @@ -0,0 +1,11 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist" + }, + "exclude": [ + "node_modules", + "dist" + ] +} \ No newline at end of file diff --git a/packages/dashboard/.env.example b/packages/dashboard/.env.example new file mode 100644 index 0000000..033bdd2 --- /dev/null +++ b/packages/dashboard/.env.example @@ -0,0 +1,2 @@ +NEXT_PUBLIC_API_URI=http://localhost:8080 +NEXT_PUBLIC_AWS_REGION=eu-west-3 \ No newline at end of file diff --git a/packages/dashboard/next-env.d.ts b/packages/dashboard/next-env.d.ts new file mode 100644 index 0000000..4f11a03 --- /dev/null +++ b/packages/dashboard/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/basic-features/typescript for more information. diff --git a/packages/dashboard/next.config.js b/packages/dashboard/next.config.js new file mode 100644 index 0000000..3d5b55d --- /dev/null +++ b/packages/dashboard/next.config.js @@ -0,0 +1,18 @@ +/** @type {import('next').NextConfig} */ +module.exports = { + reactStrictMode: true, + swcMinify: true, + webpack(config) { + config.module.rules.push({ + test: /\.svg$/, + use: ["@svgr/webpack"], + }); + + config.module.rules.push({ + test: [/src\/(components|layouts)\/index.ts/i], + sideEffects: false, + }); + + return config; + }, +}; diff --git a/packages/dashboard/package.json b/packages/dashboard/package.json new file mode 100644 index 0000000..910d9bf --- /dev/null +++ b/packages/dashboard/package.json @@ -0,0 +1,70 @@ +{ + "name": "@plunk/dashboard", + "version": "1.0.0", + "private": true, + "scripts": { + "dev": "next dev -p 3000", + "build": "next build", + "start": "next start", + "clean": "rimraf .next node_modules .turbo" + }, + "dependencies": { + "@hookform/resolvers": "^3.9.0", + "@monaco-editor/react": "^4.6.0", + "@plunk/shared": "1.0.0", + "@svgr/webpack": "^8.1.0", + "@tippyjs/react": "^4.2.6", + "@tiptap/core": "^2.5.4", + "@tiptap/extension-color": "^2.5.4", + "@tiptap/extension-dropcursor": "^2.5.4", + "@tiptap/extension-font-family": "^2.5.4", + "@tiptap/extension-image": "^2.5.4", + "@tiptap/extension-link": "^2.5.4", + "@tiptap/extension-placeholder": "^2.5.4", + "@tiptap/extension-text-align": "^2.5.4", + "@tiptap/extension-text-style": "^2.5.4", + "@tiptap/extension-typography": "^2.5.4", + "@tiptap/pm": "^2.5.4", + "@tiptap/react": "^2.5.4", + "@tiptap/starter-kit": "^2.5.4", + "@tiptap/suggestion": "^2.5.4", + "@uiball/loaders": "^1.3.1", + "classnames": "^2.5.1", + "dayjs": "^1.11.12", + "framer-motion": "^11.3.7", + "jotai": "2.9.0", + "lucide-react": "^0.408.0", + "next": "14.2.5", + "next-seo": "^6.5.0", + "nprogress": "^0.2.0", + "prosemirror-commands": "^1.5.2", + "prosemirror-dropcursor": "^1.8.1", + "prosemirror-gapcursor": "^1.3.2", + "prosemirror-history": "^1.4.1", + "prosemirror-keymap": "^1.2.2", + "prosemirror-schema-list": "^1.4.1", + "react": "18.3.1", + "react-dom": "18.3.1", + "react-hook-form": "^7.52.1", + "react-syntax-highlighter": "^15.5.0", + "recharts": "^2.12.7", + "sharp": "^0.33.4", + "sonner": "^1.5.0", + "swr": "2.2.5", + "tailwind-scrollbar": "^3.1.0", + "zod": "^3.23.8" + }, + "devDependencies": { + "@tailwindcss/aspect-ratio": "^0.4.2", + "@tailwindcss/forms": "^0.5.7", + "@tailwindcss/typography": "^0.5.13", + "@types/node": "20.14.11", + "@types/nprogress": "^0.2.3", + "@types/react": "18.3.3", + "@types/react-syntax-highlighter": "^15.5.13", + "autoprefixer": "^10.4.19", + "postcss": "^8.4.39", + "tailwindcss": "^3.4.6", + "typescript": "5.5.3" + } +} diff --git a/packages/dashboard/postcss.config.js b/packages/dashboard/postcss.config.js new file mode 100644 index 0000000..12a703d --- /dev/null +++ b/packages/dashboard/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/packages/dashboard/public/assets/card.png b/packages/dashboard/public/assets/card.png new file mode 100644 index 0000000..5a1c256 Binary files /dev/null and b/packages/dashboard/public/assets/card.png differ diff --git a/packages/dashboard/public/assets/logo.png b/packages/dashboard/public/assets/logo.png new file mode 100644 index 0000000..0022f5a Binary files /dev/null and b/packages/dashboard/public/assets/logo.png differ diff --git a/packages/dashboard/public/assets/shared.svg b/packages/dashboard/public/assets/shared.svg new file mode 100644 index 0000000..728750d --- /dev/null +++ b/packages/dashboard/public/assets/shared.svg @@ -0,0 +1,158 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/packages/dashboard/public/favicon/android-chrome-192x192.png b/packages/dashboard/public/favicon/android-chrome-192x192.png new file mode 100644 index 0000000..4f87bc3 Binary files /dev/null and b/packages/dashboard/public/favicon/android-chrome-192x192.png differ diff --git a/packages/dashboard/public/favicon/android-chrome-512x512.png b/packages/dashboard/public/favicon/android-chrome-512x512.png new file mode 100644 index 0000000..8583fd9 Binary files /dev/null and b/packages/dashboard/public/favicon/android-chrome-512x512.png differ diff --git a/packages/dashboard/public/favicon/apple-touch-icon.png b/packages/dashboard/public/favicon/apple-touch-icon.png new file mode 100644 index 0000000..6026be4 Binary files /dev/null and b/packages/dashboard/public/favicon/apple-touch-icon.png differ diff --git a/packages/dashboard/public/favicon/browserconfig.xml b/packages/dashboard/public/favicon/browserconfig.xml new file mode 100644 index 0000000..2988b2a --- /dev/null +++ b/packages/dashboard/public/favicon/browserconfig.xml @@ -0,0 +1,9 @@ + + + + + + #ffffff + + + diff --git a/packages/dashboard/public/favicon/favicon-16x16.png b/packages/dashboard/public/favicon/favicon-16x16.png new file mode 100644 index 0000000..1f715d6 Binary files /dev/null and b/packages/dashboard/public/favicon/favicon-16x16.png differ diff --git a/packages/dashboard/public/favicon/favicon-32x32.png b/packages/dashboard/public/favicon/favicon-32x32.png new file mode 100644 index 0000000..0f6f0eb Binary files /dev/null and b/packages/dashboard/public/favicon/favicon-32x32.png differ diff --git a/packages/dashboard/public/favicon/favicon.ico b/packages/dashboard/public/favicon/favicon.ico new file mode 100644 index 0000000..00615b3 Binary files /dev/null and b/packages/dashboard/public/favicon/favicon.ico differ diff --git a/packages/dashboard/public/favicon/mstile-150x150.png b/packages/dashboard/public/favicon/mstile-150x150.png new file mode 100644 index 0000000..472b468 Binary files /dev/null and b/packages/dashboard/public/favicon/mstile-150x150.png differ diff --git a/packages/dashboard/public/favicon/safari-pinned-tab.svg b/packages/dashboard/public/favicon/safari-pinned-tab.svg new file mode 100644 index 0000000..4e7acb9 --- /dev/null +++ b/packages/dashboard/public/favicon/safari-pinned-tab.svg @@ -0,0 +1,73 @@ + + + + + Created by potrace 1.14, written by Peter Selinger 2001-2017 + + + + + diff --git a/packages/dashboard/public/favicon/site.webmanifest b/packages/dashboard/public/favicon/site.webmanifest new file mode 100644 index 0000000..5eb5e44 --- /dev/null +++ b/packages/dashboard/public/favicon/site.webmanifest @@ -0,0 +1,19 @@ +{ + "name": "", + "short_name": "", + "icons": [ + { + "src": "/favicon/android-chrome-192x192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "/favicon/android-chrome-512x512.png", + "sizes": "512x512", + "type": "image/png" + } + ], + "theme_color": "#ffffff", + "background_color": "#ffffff", + "display": "standalone" +} diff --git a/packages/dashboard/src/components/Alert/Alert.tsx b/packages/dashboard/src/components/Alert/Alert.tsx new file mode 100644 index 0000000..654804f --- /dev/null +++ b/packages/dashboard/src/components/Alert/Alert.tsx @@ -0,0 +1,32 @@ +import React from 'react'; + +export interface AlertProps { + type: 'info' | 'danger' | 'warning' | 'success'; + title: string; + children?: string | React.ReactNode; +} + +const styles = { + info: 'bg-blue-50 text-blue-800 border-blue-300', + danger: 'bg-red-50 text-red-800 border-red-300', + warning: 'bg-yellow-50 text-yellow-800 border-yellow-300', + success: 'bg-green-50 text-green-800 border-green-300', +}; + +/** + * @param root0 + * @param root0.type + * @param root0.title + * @param root0.children + */ +export default function Alert({type = 'info', title, children}: AlertProps) { + const classNames = ['w-full px-7 py-5 border rounded-lg']; + classNames.push(styles[type]); + + return ( +
    +

    {title}

    +

    {children}

    +
    + ); +} diff --git a/packages/dashboard/src/components/Alert/index.tsx b/packages/dashboard/src/components/Alert/index.tsx new file mode 100644 index 0000000..b7ae192 --- /dev/null +++ b/packages/dashboard/src/components/Alert/index.tsx @@ -0,0 +1 @@ +export {default as Alert} from './Alert'; diff --git a/packages/dashboard/src/components/Badge/Badge.tsx b/packages/dashboard/src/components/Badge/Badge.tsx new file mode 100644 index 0000000..7a6bc26 --- /dev/null +++ b/packages/dashboard/src/components/Badge/Badge.tsx @@ -0,0 +1,26 @@ +import React from 'react'; + +export interface BadgeProps { + type: 'info' | 'danger' | 'warning' | 'success' | 'purple'; + children: string; +} + +const styles = { + info: 'bg-blue-100 text-blue-800', + danger: 'bg-red-100 text-red-800', + warning: 'bg-yellow-100 text-yellow-800', + success: 'bg-green-100 text-green-800', + purple: 'bg-purple-100 text-purple-800', +}; + +/** + * @param root0 + * @param root0.type + * @param root0.children + */ +export default function Badge({type = 'info', children}: BadgeProps) { + const classNames = ['inline-flex items-center px-2 py-0.5 rounded text-xs font-medium']; + classNames.push(styles[type]); + + return {children}; +} diff --git a/packages/dashboard/src/components/Badge/index.tsx b/packages/dashboard/src/components/Badge/index.tsx new file mode 100644 index 0000000..d54c868 --- /dev/null +++ b/packages/dashboard/src/components/Badge/index.tsx @@ -0,0 +1 @@ +export {default as Badge} from './Badge'; diff --git a/packages/dashboard/src/components/Card/Card.tsx b/packages/dashboard/src/components/Card/Card.tsx new file mode 100644 index 0000000..f121152 --- /dev/null +++ b/packages/dashboard/src/components/Card/Card.tsx @@ -0,0 +1,101 @@ +import React, {MutableRefObject, useEffect, useState} from 'react'; +import {AnimatePresence, motion} from 'framer-motion'; + +export interface CardProps extends React.HTMLAttributes { + title?: string; + description?: string; + actions?: React.ReactNode; + options?: React.ReactNode; +} + +/** + * @param root0 + * @param root0.title + * @param root0.description + * @param root0.children + * @param root0.className + * @param root0.actions + * @param root0.options + */ +export default function Card({title, description, children, className, actions, options}: CardProps) { + const ref = React.createRef(); + + const [optionsOpen, setOptionsOpen] = useState(false); + + useEffect(() => { + const mutableRef = ref as MutableRefObject; + + const handleClickOutside = (event: any) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + if (mutableRef.current && !mutableRef.current.contains(event.target) && optionsOpen) { + setOptionsOpen(false); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [ref]); + + return ( +
    +
    +
    +
    +

    {title}

    +

    {description}

    +
    +
    {actions}
    +
    + + {options && ( +
    +
    + +
    + + + {optionsOpen && ( + + {options} + + )} + +
    + )} +
    + +
    {children}
    +
    + ); +} diff --git a/packages/dashboard/src/components/Card/index.tsx b/packages/dashboard/src/components/Card/index.tsx new file mode 100644 index 0000000..afd6874 --- /dev/null +++ b/packages/dashboard/src/components/Card/index.tsx @@ -0,0 +1 @@ +export {default as Card} from './Card'; diff --git a/packages/dashboard/src/components/CodeBlock/CodeBlock.tsx b/packages/dashboard/src/components/CodeBlock/CodeBlock.tsx new file mode 100644 index 0000000..727873f --- /dev/null +++ b/packages/dashboard/src/components/CodeBlock/CodeBlock.tsx @@ -0,0 +1,118 @@ +import SyntaxHighlighter from 'react-syntax-highlighter'; +import React from 'react'; + +export interface CodeBlockProps { + language: string; + code: string; + style?: React.CSSProperties; +} + +/** + * + * @param root0 + * @param root0.code + * @param root0.language + * @param root0.style + */ +export default function ({code, language, style}: CodeBlockProps) { + return ( + + {code} + + ); +} diff --git a/packages/dashboard/src/components/CodeBlock/index.tsx b/packages/dashboard/src/components/CodeBlock/index.tsx new file mode 100644 index 0000000..e556e1b --- /dev/null +++ b/packages/dashboard/src/components/CodeBlock/index.tsx @@ -0,0 +1 @@ +export {default as CodeBlock} from './CodeBlock'; diff --git a/packages/dashboard/src/components/Input/Dropdown/Dropdown.tsx b/packages/dashboard/src/components/Input/Dropdown/Dropdown.tsx new file mode 100644 index 0000000..b6a758d --- /dev/null +++ b/packages/dashboard/src/components/Input/Dropdown/Dropdown.tsx @@ -0,0 +1,191 @@ +import {AnimatePresence, motion} from 'framer-motion'; +import React, {MutableRefObject, useEffect, useState} from 'react'; + +export interface Dropdownprops { + withSearch?: boolean; + inModal?: boolean; + disabled?: boolean; + onChange: (value: string) => void; + values: { + name: string; + value: string; + }[]; + selectedValue: string; + className?: string; +} + +/** + * @param root0 + * @param root0.onChange + * @param root0.values + * @param root0.selectedValue + * @param root0.className + * @param root0.withSearch + * @param root0.inModal + * @param root0.disabled + */ +export default function Dropdown({ + onChange, + values, + selectedValue, + className, + withSearch = false, + inModal = false, + disabled = false, +}: Dropdownprops) { + const [open, setOpen] = useState(false); + const ref = React.createRef(); + + useEffect(() => { + const mutableRef = ref as MutableRefObject; + + const handleClickOutside = (event: any) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + if (mutableRef.current && !mutableRef.current.contains(event.target) && open) { + setOpen(false); + setQuery(''); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [ref]); + + const [query, setQuery] = useState(''); + + return ( +
    +
    + + + + {open && ( + +
    + {withSearch ? ( + <> +
  • + setQuery(e.target.value)} + /> +
  • +
    + + ) : null} +
    + +
    + {values.filter(value => value.name.toLowerCase().startsWith(query.toLowerCase())).length === 0 ? ( +
  • + No results found +
  • + ) : ( + values + .filter(value => value.name.toLowerCase().startsWith(query.toLowerCase())) + .map((value, index) => { + return ( +
  • { + onChange(value.value); + setQuery(''); + setOpen(!open); + }} + > + + {value.name.charAt(0).toUpperCase() + value.name.slice(1).toLowerCase()} + + {value.value === selectedValue ? ( + + + + ) : null} +
  • + ); + }) + )} +
    +
    + )} +
    +
    +
    + ); +} diff --git a/packages/dashboard/src/components/Input/Dropdown/index.tsx b/packages/dashboard/src/components/Input/Dropdown/index.tsx new file mode 100644 index 0000000..c8ef1a6 --- /dev/null +++ b/packages/dashboard/src/components/Input/Dropdown/index.tsx @@ -0,0 +1 @@ +export {default as Dropdown} from './Dropdown'; diff --git a/packages/dashboard/src/components/Input/Input/Input.tsx b/packages/dashboard/src/components/Input/Input/Input.tsx new file mode 100644 index 0000000..6e0ef32 --- /dev/null +++ b/packages/dashboard/src/components/Input/Input/Input.tsx @@ -0,0 +1,57 @@ +import {FieldError, UseFormRegisterReturn} from 'react-hook-form'; +import {AnimatePresence, motion} from 'framer-motion'; +import React from 'react'; + +export interface InputProps { + label?: string; + placeholder?: string; + type?: 'text' | 'email' | 'password' | 'number'; + register: UseFormRegisterReturn; + error?: FieldError; + className?: string; + min?: number; + max?: number; +} + +/** + * + * @param props + * @param props.label + * @param props.type + * @param props.register + * @param props.error + * @param props.placeholder + * @param props.className + */ +export default function Input(props: InputProps) { + return ( +
    + +
    + +
    + + {props.error && ( + + {props.error.message} + + )} + +
    + ); +} diff --git a/packages/dashboard/src/components/Input/Input/index.ts b/packages/dashboard/src/components/Input/Input/index.ts new file mode 100644 index 0000000..f3185ce --- /dev/null +++ b/packages/dashboard/src/components/Input/Input/index.ts @@ -0,0 +1 @@ +export {default as Input} from './Input'; diff --git a/packages/dashboard/src/components/Input/MarkdownEditor/Editor.tsx b/packages/dashboard/src/components/Input/MarkdownEditor/Editor.tsx new file mode 100644 index 0000000..e9078da --- /dev/null +++ b/packages/dashboard/src/components/Input/MarkdownEditor/Editor.tsx @@ -0,0 +1,873 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import Image from "@tiptap/extension-image"; +import Link from "@tiptap/extension-link"; +import Placeholder from "@tiptap/extension-placeholder"; +import Typography from "@tiptap/extension-typography"; +import { EditorContent, useEditor } from "@tiptap/react"; +import StarterKit from "@tiptap/starter-kit"; +import { AnimatePresence, motion } from "framer-motion"; +import React, { useCallback, useRef, useState } from "react"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; +import { API_URI } from "../../../lib/constants"; +import { Modal } from "../../Overlay"; +import "tippy.js/animations/scale.css"; +import HTMLEditor from "@monaco-editor/react"; +import { Color } from "@tiptap/extension-color"; +import { Dropcursor } from "@tiptap/extension-dropcursor"; +import FontFamily from "@tiptap/extension-font-family"; +import { TextAlign } from "@tiptap/extension-text-align"; +import { TextStyle } from "@tiptap/extension-text-style"; +import { + AlignCenter, + AlignLeft, + AlignRight, + ImageIcon, + Inspect, + LinkIcon, +} from "lucide-react"; +import { toast } from "sonner"; +import { Dropdown } from "../Dropdown"; +import { Button } from "./extensions/Button"; +import { EditorBubbleMenu } from "./extensions/EditorBubbleMenu"; +import { Mention } from "./extensions/MetadataSuggestion/MetadataSuggestion"; +import suggestion from "./extensions/MetadataSuggestion/Suggestions"; +import { Progress, type colors } from "./extensions/Progress"; +import Slash from "./extensions/Slash"; + +export interface MarkdownEditorProps { + value: string; + mode: "PLUNK" | "HTML"; + onChange: (value: string, type: "PLUNK" | "HTML") => void; + modeSwitcher?: boolean; +} + +/** + * + * @param root0 + * @param root0.value + * @param root0.onChange + */ +export default function Editor({ + value, + onChange, + mode, + modeSwitcher, +}: MarkdownEditorProps) { + const [imageModal, setImageModal] = useState(false); + const [urlModal, setUrlModal] = useState(false); + const [barModal, setBarModal] = useState(false); + const [buttonModal, setButtonModal] = useState(false); + const [confirmModal, setConfirmModal] = useState(false); + + const fileInput = useRef(null); + + const editor = useEditor({ + extensions: [ + Slash, + StarterKit, + Typography, + TextStyle, + Mention.configure({ + HTMLAttributes: { + class: "mention", + }, + // @ts-ignore + suggestion, + }), + Dropcursor.configure({ + width: 3, + color: "#e5e5e5", + }), + TextAlign.configure({ + alignments: ["left", "center", "right"], + types: ["heading", "paragraph"], + defaultAlignment: "left", + }), + FontFamily.configure({ + types: ["textStyle"], + }), + Image.configure({ allowBase64: true }), + Placeholder.configure({ + placeholder: "Start typing or press / to use a slash command", + includeChildren: true, + }), + Progress, + Button, + Link.configure({ + autolink: true, + protocols: ["http", "https", "mailto"], + }).extend({ + addKeyboardShortcuts() { + return { + Space: ({ editor }) => { + if (editor.isActive("link")) { + // Toggle the link and add a space + editor.commands.toggleMark("link"); + // Add a space + return editor.chain().focus().insertContent(" ").run(); + } + + return false; + }, + }; + }, + }), + Color, + ], + content: value, + editorProps: { + attributes: { + class: "prose font-sans my-5 focus:outline-none", + }, + handleDOMEvents: { + keydown: (_view, event) => { + return event.key === "Enter" && !event.shiftKey; + }, + }, + }, + onUpdate: ({ editor }) => { + onChange(editor.getHTML(), "PLUNK"); + }, + }); + + const { + register: registerUrl, + handleSubmit: handleSubmitUrl, + reset: resetUrl, + setFocus: setFocusUrl, + formState: { errors: errorsUrl }, + } = useForm<{ + url: string; + }>({ + resolver: zodResolver( + z.object({ + url: z + .string() + .regex( + /^(?:https?:\/\/)?(?:\{\{[\w-]+}}|(?:[\w-]+\.)+[a-z]{2,})(?:\/\S*)?(?:\?\S*)?$/, + ) + .transform((u) => { + if (u.startsWith("{{") && u.endsWith("}}")) { + return u; + } + + return u.startsWith("http") ? u : `https://${u}`; + }), + }), + ), + }); + + const { + register: registerBar, + handleSubmit: handleSubmitBar, + reset: resetBar, + setValue: setValueBar, + watch: watchBar, + formState: { errors: errorsBar }, + } = useForm<{ + percent: number; + color: colors; + }>({ + resolver: zodResolver( + z.object({ + percent: z.preprocess( + (a) => Number.parseInt(z.string().parse(a), 10), + z.number().positive().max(100), + ), + color: z + .enum([ + "red", + "yellow", + "green", + "blue", + "indigo", + "purple", + "pink", + "orange", + "black", + ]) + .default("blue"), + }), + ), + defaultValues: { + color: "blue", + }, + }); + + const { + register: registerButton, + handleSubmit: handleSubmitButton, + reset: resetButton, + setValue: setValueButton, + watch: watchButton, + formState: { errors: errorsButton }, + } = useForm<{ + link: string; + color: colors; + }>({ + resolver: zodResolver( + z.object({ + link: z + .string() + .regex( + /^(?:https?:\/\/)?(?:\{\{[\w-]+}}|(?:[\w-]+\.)+[a-z]{2,})(?:\/\S*)?$/, + ) + .transform((u) => { + if (u.startsWith("{{") && u.endsWith("}}")) { + return u; + } + + return u.startsWith("http") ? u : `https://${u}`; + }), + color: z + .enum([ + "red", + "yellow", + "green", + "blue", + "indigo", + "purple", + "pink", + "orange", + "black", + ]) + .default("blue"), + }), + ), + defaultValues: { + color: "blue", + }, + }); + + const addImage = useCallback( + (data: { url: string }) => { + editor?.chain().focus().setImage({ src: data.url }).run(); + setImageModal(false); + }, + [editor], + ); + + const addBar = useCallback( + (data: { percent: number; color: colors }) => { + editor + ?.chain() + .focus() + .setProgress({ percent: data.percent, color: data.color }) + .run(); + setBarModal(false); + resetBar(); + }, + [editor, resetBar], + ); + + const addButton = useCallback( + (data: { link: string; color: colors }) => { + editor + ?.chain() + .focus() + .setButton({ href: data.link, color: data.color }) + .run(); + setButtonModal(false); + resetButton(); + }, + [editor, resetButton], + ); + + const addUrl = useCallback( + (data: { url: string }) => { + editor + ?.chain() + .focus() + .setLink({ href: data.url, target: "_blank" }) + .run(); + setUrlModal(false); + resetUrl(); + }, + [editor, resetUrl], + ); + + if (!editor) { + return null; + } + + return ( + <> + setConfirmModal(!confirmModal)} + onAction={() => { + if (mode === "PLUNK") { + void onChange("", "HTML"); + } else { + void onChange("", "PLUNK"); + } + + editor.chain().clearContent().run(); + setConfirmModal(false); + }} + type={"danger"} + > +
    +

    + Are you sure you want to switch to{" "} + {mode === "PLUNK" ? "HTML" : "the Plunk Editor"}?
    This will + clear your current content. +

    +
    +
    + {modeSwitcher && ( +
    + + {" "} +
    + )} + + setImageModal(!imageModal)} + onAction={handleSubmitUrl(addImage)} + type={"info"} + action={"Add"} + icon={ + <> + + + } + > +
    +
    + +
    + +
    + + {errorsUrl.url?.message && ( + + {errorsUrl.url.message} + + )} + +
    +
    +
    + setUrlModal(!urlModal)} + onAction={handleSubmitUrl(addUrl)} + type={"info"} + action={"Add"} + icon={ + <> + + + + + } + > +
    { + if (e.key === "Enter") { + e.preventDefault(); + void handleSubmitUrl(addUrl)(); + } + }} + className="grid gap-6 sm:grid-cols-2" + > +
    + +
    + + https:// + + +
    + + {errorsUrl.url?.message && ( + + {errorsUrl.url.message} + + )} + +
    +
    +
    + setBarModal(!barModal)} + onAction={handleSubmitBar(addBar)} + type={"info"} + action={"Add"} + icon={ + <> + + + + + + } + > +
    { + if (e.key === "Enter") { + e.preventDefault(); + void handleSubmitUrl(addUrl)(); + } + }} + className="grid gap-6 sm:grid-cols-2" + > +
    + +
    + +
    + + {errorsBar.percent?.message && ( + + {errorsBar.percent.message} + + )} + +
    + +
    + + setValueBar("color", t as colors)} + values={[ + { value: "blue", name: "Blue" }, + { value: "red", name: "Red" }, + { value: "green", name: "Green" }, + { value: "yellow", name: "Yellow" }, + { value: "orange", name: "Orange" }, + { value: "purple", name: "Purple" }, + { value: "pink", name: "Pink" }, + { value: "indigo", name: "Indigo" }, + ]} + selectedValue={watchBar("color")} + /> + + {errorsBar.color?.message && ( + + {errorsBar.color.message} + + )} + +
    +
    +
    + setButtonModal(!buttonModal)} + onAction={handleSubmitButton(addButton)} + type={"info"} + action={"Add"} + icon={ + <> + + + + + + + + + + } + > +
    { + if (e.key === "Enter") { + e.preventDefault(); + void handleSubmitUrl(addUrl)(); + } + }} + className="grid gap-6 sm:grid-cols-2" + > +
    + +
    + + https:// + + +
    + + {errorsButton.link?.message && ( + + {errorsButton.link.message} + + )} + +
    + +
    + + setValueButton("color", t as colors)} + values={[ + { value: "blue", name: "Blue" }, + { value: "red", name: "Red" }, + { value: "green", name: "Green" }, + { value: "yellow", name: "Yellow" }, + { value: "orange", name: "Orange" }, + { value: "purple", name: "Purple" }, + { value: "pink", name: "Pink" }, + { value: "indigo", name: "Indigo" }, + { value: "black", name: "Black" }, + ]} + selectedValue={watchButton("color")} + /> + + {errorsButton.color?.message && ( + + {errorsButton.color.message} + + )} + +
    +
    +
    +
    + <> + {mode === "PLUNK" ? ( + <> +
    { + editor.chain().focus().run(); + }} + > + +
    +
    +
    +
    +
    + + + +
    + +
    + + + +
    +
    +
    + <> +
    +
    + + { + setUrlModal(true); + setTimeout(() => { + setFocusUrl("url", { shouldSelect: true }); + }, 100); + }, + isActive: () => false, + }, + ]} + /> +
    +
    + +
    +
    +
    + + ) : ( + <> +
    +
    + +
    + onChange(e as string, "HTML")} + options={{ + inlineSuggest: true, + fontSize: "12px", + formatOnType: true, + autoClosingBrackets: true, + minimap: { + enabled: false, + }, + }} + /> +
    +
    + +
    + + +
    +
    +
    +
    +
    + + )} + +
    + + ); +} diff --git a/packages/dashboard/src/components/Input/MarkdownEditor/extensions/Button.tsx b/packages/dashboard/src/components/Input/MarkdownEditor/extensions/Button.tsx new file mode 100644 index 0000000..18b1a0c --- /dev/null +++ b/packages/dashboard/src/components/Input/MarkdownEditor/extensions/Button.tsx @@ -0,0 +1,109 @@ +import {mergeAttributes, Node, wrappingInputRule} from '@tiptap/core'; + +export type colors = 'red' | 'orange' | 'yellow' | 'green' | 'blue' | 'indigo' | 'purple' | 'pink' | 'black'; + +// Map each color to a tailwind color hex code for 500 +const colorMap = { + red: '#ef4444', + orange: '#f97316', + yellow: '#facc15', + green: '#22c55e', + blue: '#2563eb', + indigo: '#6366f1', + purple: '#8b5cf6', + pink: '#ec4899', + black: '#171717', +} as const; + +export interface ButtonOptions { + HTMLAttributes: Record; +} + +declare module '@tiptap/core' { + interface Commands { + button: { + /** + * Set a blockquote node + */ + setButton: (attributes: {href: string; color: colors}) => ReturnType; + /** + * Toggle a blockquote node + */ + toggleButton: (attributes: {href: string; color: colors}) => ReturnType; + }; + } +} + +export const inputRegex = /^\s*>\s$/; + +export const Button = Node.create({ + name: 'button', + content: 'text*', + marks: '', + group: 'block', + defining: true, + + addOptions() { + return { + HTMLAttributes: { + class: 'btn', + }, + }; + }, + + addAttributes() { + return { + href: { + default: null, + }, + color: { + default: 'blue' as colors, + }, + }; + }, + + parseHTML() { + return [ + { + tag: 'a.btn', + priority: 51, + }, + ]; + }, + + renderHTML({node, HTMLAttributes}) { + return [ + 'a', + mergeAttributes(this.options.HTMLAttributes, HTMLAttributes, { + style: `color: white; background-color: ${ + colorMap[node.attrs.color as colors] + }; text-align: center; text-decoration: none; padding: 12px; border-radius: 8px; display: block; font-size: 15px; line-height: 20px; font-weight: 600; margin: 9px 0 9px 0;`, + }), + 0, + ]; + }, + + addCommands() { + return { + setButton: + attributes => + ({commands}) => { + return commands.setNode(this.name, attributes); + }, + toggleButton: + attributes => + ({commands}) => { + return commands.toggleNode(this.name, 'paragraph', attributes); + }, + }; + }, + + addInputRules() { + return [ + wrappingInputRule({ + find: inputRegex, + type: this.type, + }), + ]; + }, +}); diff --git a/packages/dashboard/src/components/Input/MarkdownEditor/extensions/ColorSelector.tsx b/packages/dashboard/src/components/Input/MarkdownEditor/extensions/ColorSelector.tsx new file mode 100644 index 0000000..d266e79 --- /dev/null +++ b/packages/dashboard/src/components/Input/MarkdownEditor/extensions/ColorSelector.tsx @@ -0,0 +1,98 @@ +import {Editor} from '@tiptap/core'; +import cx from 'classnames'; +import {Check, ChevronDown} from 'lucide-react'; +import {FC} from 'react'; + +export interface BubbleColorMenuItem { + name: string; + color: string; +} + +interface ColorSelectorProps { + editor: Editor; + isOpen: boolean; + setIsOpen: (isOpen: boolean) => void; +} + +export const ColorSelector: FC = ({editor, isOpen, setIsOpen}) => { + const items: BubbleColorMenuItem[] = [ + { + name: 'Default', + color: '#000000', + }, + { + name: 'Purple', + color: '#9333EA', + }, + { + name: 'Red', + color: '#E00000', + }, + { + name: 'Blue', + color: '#2563EB', + }, + { + name: 'Green', + color: '#008A00', + }, + { + name: 'Orange', + color: '#FFA500', + }, + { + name: 'Pink', + color: '#BA4081', + }, + { + name: 'Gray', + color: '#A8A29E', + }, + ]; + + const activeItem = items.find(({color}) => editor.isActive('textStyle', {color})); + + return ( +
    + + + {isOpen && ( +
    + {items.map(({name, color}, index) => ( + + ))} +
    + )} +
    + ); +}; diff --git a/packages/dashboard/src/components/Input/MarkdownEditor/extensions/EditorBubbleMenu.tsx b/packages/dashboard/src/components/Input/MarkdownEditor/extensions/EditorBubbleMenu.tsx new file mode 100644 index 0000000..141881d --- /dev/null +++ b/packages/dashboard/src/components/Input/MarkdownEditor/extensions/EditorBubbleMenu.tsx @@ -0,0 +1,96 @@ +import {BubbleMenu, BubbleMenuProps} from '@tiptap/react'; +import cx from 'classnames'; +import {FC, useState} from 'react'; +import {BoldIcon, ItalicIcon, StrikethroughIcon} from 'lucide-react'; + +import {NodeSelector} from './NodeSelector'; +import {ColorSelector} from './ColorSelector'; + +export interface BubbleMenuItem { + name: string; + isActive: () => boolean; + command: () => void; + icon: typeof BoldIcon; +} + +type EditorBubbleMenuProps = Omit & { + items: BubbleMenuItem[]; +}; + +export const EditorBubbleMenu: FC = props => { + const items: BubbleMenuItem[] = [ + { + name: 'bold', + isActive: () => props.editor?.isActive('bold') ?? false, + command: () => props.editor?.chain().focus().toggleBold().run(), + icon: BoldIcon, + }, + { + name: 'italic', + isActive: () => props.editor?.isActive('italic') ?? false, + command: () => props.editor?.chain().focus().toggleItalic().run(), + icon: ItalicIcon, + }, + + { + name: 'strike', + isActive: () => props.editor?.isActive('strike') ?? false, + command: () => props.editor?.chain().focus().toggleStrike().run(), + icon: StrikethroughIcon, + }, + ...props.items, + ]; + + const bubbleMenuProps: EditorBubbleMenuProps = { + ...props, + shouldShow: ({editor}) => { + // don't show if image is selected + if (editor.isActive('image')) { + return false; + } + return editor.view.state.selection.content().size > 0; + }, + tippyOptions: { + moveTransition: 'transform 0.15s ease-out', + onHidden: () => { + setIsNodeSelectorOpen(false); + setIsColorSelectorOpen(false); + }, + }, + }; + + const [isNodeSelectorOpen, setIsNodeSelectorOpen] = useState(false); + const [isColorSelectorOpen, setIsColorSelectorOpen] = useState(false); + + return ( + + {props.editor && ( + + )} + + {items.map((item, index) => ( + + ))} + + {props.editor && ( + + )} + + ); +}; diff --git a/packages/dashboard/src/components/Input/MarkdownEditor/extensions/MetadataSuggestion/MetadataSuggestion.tsx b/packages/dashboard/src/components/Input/MarkdownEditor/extensions/MetadataSuggestion/MetadataSuggestion.tsx new file mode 100644 index 0000000..d58ef00 --- /dev/null +++ b/packages/dashboard/src/components/Input/MarkdownEditor/extensions/MetadataSuggestion/MetadataSuggestion.tsx @@ -0,0 +1,151 @@ +import {mergeAttributes, Node} from '@tiptap/core'; +import {Node as ProseMirrorNode} from '@tiptap/pm/model'; +import {PluginKey} from '@tiptap/pm/state'; +import Suggestion, {SuggestionOptions} from '@tiptap/suggestion'; + +export interface MentionOptions { + HTMLAttributes: Record; + renderLabel: (props: {options: MentionOptions; node: ProseMirrorNode}) => string; + suggestion: Omit; +} + +export const MentionPluginKey = new PluginKey('mention'); + +export const Mention = Node.create({ + name: 'mention', + + addOptions() { + return { + HTMLAttributes: {}, + renderLabel({options, node}) { + return `${options.suggestion.char}${node.attrs.label ?? node.attrs.id}`; + }, + suggestion: { + char: '{{', + pluginKey: MentionPluginKey, + command: ({editor, range, props}) => { + // increase range.to by one when the next node is of type "text" + // and starts with a space character + const nodeAfter = editor.view.state.selection.$to.nodeAfter; + const overrideSpace = nodeAfter?.text?.startsWith(' '); + + if (overrideSpace) { + range.to += 1; + } + + editor.chain().focus().insertContent(`${props.id}}}`).run(); + + window.getSelection()?.collapseToEnd(); + }, + allow: ({state, range}) => { + const $from = state.doc.resolve(range.from); + const type = state.schema.nodes[this.name]; + const allow = !!$from.parent.type.contentMatch.matchType(type); + + return allow; + }, + }, + }; + }, + + group: 'inline', + + inline: true, + + selectable: true, + + atom: true, + + addAttributes() { + return { + id: { + default: null, + parseHTML: element => element.getAttribute('data-id'), + renderHTML: attributes => { + if (!attributes.id) { + return {}; + } + + return { + 'data-id': attributes.id, + }; + }, + }, + + label: { + default: null, + parseHTML: element => element.getAttribute('data-label'), + renderHTML: attributes => { + if (!attributes.label) { + return {}; + } + + return { + 'data-label': attributes.label, + }; + }, + }, + }; + }, + + parseHTML() { + return [ + { + tag: `span[data-type="${this.name}"]`, + }, + ]; + }, + + renderHTML({node, HTMLAttributes}) { + return [ + 'span', + mergeAttributes({'data-type': this.name}, this.options.HTMLAttributes, HTMLAttributes), + this.options.renderLabel({ + options: this.options, + node, + }), + ]; + }, + + renderText({node}) { + return this.options.renderLabel({ + options: this.options, + node, + }); + }, + + addKeyboardShortcuts() { + return { + Backspace: () => + this.editor.commands.command(({tr, state}) => { + let isMention = false; + const {selection} = state; + const {empty, anchor} = selection; + + if (!empty) { + return false; + } + + state.doc.nodesBetween(anchor - 1, anchor, (node, pos) => { + if (node.type.name === this.name) { + isMention = true; + tr.insertText(this.options.suggestion.char ?? '', pos, pos + node.nodeSize); + + return false; + } + }); + + return isMention; + }), + }; + }, + + addProseMirrorPlugins() { + return [ + Suggestion({ + editor: this.editor, + ...this.options.suggestion, + }), + ]; + }, +}); diff --git a/packages/dashboard/src/components/Input/MarkdownEditor/extensions/MetadataSuggestion/SuggestionList.tsx b/packages/dashboard/src/components/Input/MarkdownEditor/extensions/MetadataSuggestion/SuggestionList.tsx new file mode 100644 index 0000000..1334952 --- /dev/null +++ b/packages/dashboard/src/components/Input/MarkdownEditor/extensions/MetadataSuggestion/SuggestionList.tsx @@ -0,0 +1,79 @@ +// @ts-nocheck + +import React, { + forwardRef, + useEffect, + useImperativeHandle, + useState, +} from "react"; + +export default forwardRef((props, ref) => { + const [selectedIndex, setSelectedIndex] = useState(0); + + const selectItem = (index) => { + const item = props.items[index]; + + if (item) { + props.command({ id: item }); + } + }; + + const upHandler = () => { + setSelectedIndex( + (selectedIndex + props.items.length - 1) % props.items.length, + ); + }; + + const downHandler = () => { + setSelectedIndex((selectedIndex + 1) % props.items.length); + }; + + const enterHandler = () => { + selectItem(selectedIndex); + }; + + useEffect(() => setSelectedIndex(0), [props.items]); + + useImperativeHandle(ref, () => ({ + onKeyDown: ({ event }) => { + if (event.key === "ArrowUp") { + upHandler(); + return true; + } + + if (event.key === "ArrowDown") { + downHandler(); + return true; + } + + if (event.key === "Enter") { + enterHandler(); + return true; + } + + return false; + }, + })); + + return ( +
    + {props.items.length ? ( + props.items.map((item, index) => ( + + )) + ) : ( +
    + No result +
    + )} +
    + ); +}); diff --git a/packages/dashboard/src/components/Input/MarkdownEditor/extensions/MetadataSuggestion/Suggestions.ts b/packages/dashboard/src/components/Input/MarkdownEditor/extensions/MetadataSuggestion/Suggestions.ts new file mode 100644 index 0000000..77054af --- /dev/null +++ b/packages/dashboard/src/components/Input/MarkdownEditor/extensions/MetadataSuggestion/Suggestions.ts @@ -0,0 +1,87 @@ +// @ts-nocheck + +import { ReactRenderer } from "@tiptap/react"; +import { network } from "dashboard/src/lib/network"; +import type { RefAttributes } from "react"; +import tippy from "tippy.js"; +import MentionList from "./SuggestionList"; + +export default { + items: async ({ query }: { query: string }) => { + const activeProject = + typeof window !== "undefined" + ? window.localStorage.getItem("project") + : null; + + if (!activeProject) { + return []; + } + + const keys = await network.fetch( + "GET", + `/projects/id/${activeProject}/contacts/metadata`, + ); + + return keys.filter((key) => + key.toLowerCase().includes(query.toLowerCase()), + ); + }, + + render: () => { + let component: ReactRenderer>; + let popup: { destroy: () => void }[]; + + return { + onStart: (props: { editor: any; clientRect: any }) => { + component = new ReactRenderer(MentionList, { + props, + editor: props.editor, + }); + + if (!props.clientRect) { + return; + } + + popup = tippy("body", { + getReferenceClientRect: props.clientRect, + appendTo: () => document.body, + content: component.element, + showOnCreate: true, + interactive: true, + trigger: "manual", + placement: "bottom-start", + }); + }, + + onUpdate(props) { + component.updateProps(props); + + if (!props.clientRect) { + return; + } + + popup[0].setProps({ + getReferenceClientRect: props.clientRect, + }); + }, + + onKeyDown(props) { + if (props.event.key === "Escape") { + popup[0].hide(); + + return true; + } + + return component.ref?.onKeyDown(props); + }, + + onExit() { + if (popup[0]) { + popup[0].destroy(); + } + + component.destroy(); + }, + }; + }, +}; diff --git a/packages/dashboard/src/components/Input/MarkdownEditor/extensions/NodeSelector.tsx b/packages/dashboard/src/components/Input/MarkdownEditor/extensions/NodeSelector.tsx new file mode 100644 index 0000000..0efd877 --- /dev/null +++ b/packages/dashboard/src/components/Input/MarkdownEditor/extensions/NodeSelector.tsx @@ -0,0 +1,100 @@ +import {Editor} from '@tiptap/core'; +import cx from 'classnames'; +import {Check, ChevronDown, Heading1, Heading2, Heading3, ListOrdered, TextIcon} from 'lucide-react'; +import {FC} from 'react'; + +import {BubbleMenuItem} from './EditorBubbleMenu'; + +interface NodeSelectorProps { + editor: Editor; + isOpen: boolean; + setIsOpen: (isOpen: boolean) => void; +} + +export const NodeSelector: FC = ({editor, isOpen, setIsOpen}) => { + const items: BubbleMenuItem[] = [ + { + name: 'Text', + icon: TextIcon, + command: () => editor.chain().focus().toggleNode('paragraph', 'paragraph').run(), + isActive: () => editor.isActive('paragraph') && !editor.isActive('bulletList') && !editor.isActive('orderedList'), + }, + { + name: 'Heading 1', + icon: Heading1, + command: () => editor.chain().focus().toggleHeading({level: 1}).run(), + isActive: () => editor.isActive('heading', {level: 1}), + }, + { + name: 'Heading 2', + icon: Heading2, + command: () => editor.chain().focus().toggleHeading({level: 2}).run(), + isActive: () => editor.isActive('heading', {level: 2}), + }, + { + name: 'Heading 3', + icon: Heading3, + command: () => editor.chain().focus().toggleHeading({level: 3}).run(), + isActive: () => editor.isActive('heading', {level: 3}), + }, + { + name: 'Bullet List', + icon: ListOrdered, + command: () => editor.chain().focus().toggleBulletList().run(), + isActive: () => editor.isActive('bulletList'), + }, + { + name: 'Numbered List', + icon: ListOrdered, + command: () => editor.chain().focus().toggleOrderedList().run(), + isActive: () => editor.isActive('orderedList'), + }, + ]; + + const activeItem = items.find(item => item.isActive()); + + return ( +
    + + + {isOpen && ( +
    + {items.map((item, index) => ( + + ))} +
    + )} +
    + ); +}; diff --git a/packages/dashboard/src/components/Input/MarkdownEditor/extensions/Progress.tsx b/packages/dashboard/src/components/Input/MarkdownEditor/extensions/Progress.tsx new file mode 100644 index 0000000..d40543c --- /dev/null +++ b/packages/dashboard/src/components/Input/MarkdownEditor/extensions/Progress.tsx @@ -0,0 +1,133 @@ +import {Node} from '@tiptap/core'; + +export type colors = 'red' | 'orange' | 'yellow' | 'green' | 'blue' | 'indigo' | 'purple' | 'pink' | 'black'; + +// Map each color to a tailwind color hex code for 500 +const colorMap = { + red: '#ef4444', + orange: '#f97316', + yellow: '#facc15', + green: '#22c55e', + blue: '#2563eb', + indigo: '#6366f1', + purple: '#8b5cf6', + pink: '#ec4899', + black: '#171717', +} as const; + +export interface ProgressOptions { + percent: number; + color: colors; +} + +declare module '@tiptap/core' { + interface Commands { + progress: { + /** + * Set a heading node + */ + setProgress: (attributes: {percent: number; color: colors}) => ReturnType; + /** + * Toggle a heading node + */ + toggleProgress: (attributes: {percent: number; color: colors}) => ReturnType; + }; + } +} + +export const Progress = Node.create({ + name: 'progress', + + content: 'inline*', + + group: 'block', + + defining: true, + + addAttributes() { + return { + percent: { + default: 100, + rendered: false, + }, + color: { + default: 'blue' as colors, + rendered: false, + }, + }; + }, + + parseHTML() { + return [ + { + tag: 'table', + getAttrs: element => { + // @ts-ignore + const percent = element.querySelector('td')?.style.width; + // @ts-ignore + const color = element.querySelector('td')?.style.backgroundColor; + + const rgb = color?.slice(4, color.length - 1).split(', '); + const hex = rgb?.map((value: any) => { + const hex = Number(value).toString(16); + return hex.length === 1 ? '0' + hex : hex; + }); + + return { + percent: Number(percent?.slice(0, percent.length - 1)), + color: Object.keys(colorMap).find(key => colorMap[key as colors] === `#${hex?.join('')}`) as colors, + }; + }, + }, + ]; + }, + + renderHTML({node}) { + // Render a progress bar using table elements + return [ + 'table', + { + class: 'progress', + style: `width: 100%; border-radius: 10px;height: 28px;`, + }, + [ + 'tr', + { + style: `width: 100%; border-radius: 8px;`, + }, + // Render two cells, one for the progress bar and one for the percentage + [ + 'td', + { + style: `width: ${node.attrs.percent}%; background-color: ${ + colorMap[node.attrs.color as colors] + }; border-top-left-radius: 8px; border-bottom-left-radius: 8px;`, + }, + ], + [ + 'td', + { + style: `width: ${ + 100 - node.attrs.percent + }%; background-color: #f5f5f5; border-top-right-radius: 8px; border-bottom-right-radius: 8px;`, + }, + ], + ], + ]; + }, + + addCommands() { + return { + setProgress: + attributes => + ({commands}) => { + return commands.setNode(this.name, attributes); + }, + toggleProgress: + attributes => + ({commands}) => { + return commands.toggleNode(this.name, 'paragraph', attributes); + }, + }; + }, +}); diff --git a/packages/dashboard/src/components/Input/MarkdownEditor/extensions/Slash.tsx b/packages/dashboard/src/components/Input/MarkdownEditor/extensions/Slash.tsx new file mode 100644 index 0000000..21538c7 --- /dev/null +++ b/packages/dashboard/src/components/Input/MarkdownEditor/extensions/Slash.tsx @@ -0,0 +1,350 @@ +import { type Editor, Extension, type Range } from "@tiptap/core"; +import { ReactRenderer } from "@tiptap/react"; +import Suggestion from "@tiptap/suggestion"; +import { + AlignCenter, + AlignLeft, + AlignRight, + Bold, + Code, + Heading1, + Heading2, + Heading3, + Italic, + List, + ListOrdered, + Quote, + Strikethrough, +} from "lucide-react"; +import React, { + type ReactNode, + useCallback, + useEffect, + useLayoutEffect, + useRef, + useState, +} from "react"; +import tippy from "tippy.js"; + +interface CommandItemProps { + title: string; + description: string; + icon: ReactNode; +} + +interface Command { + editor: Editor; + range: Range; +} + +const Command = Extension.create({ + name: "slash-command", + addOptions() { + return { + suggestion: { + char: "/", + command: ({ + editor, + range, + props, + }: { editor: Editor; range: Range; props: any }) => { + props.command({ editor, range }); + }, + }, + }; + }, + addProseMirrorPlugins() { + return [ + Suggestion({ + editor: this.editor, + ...this.options.suggestion, + }), + ]; + }, +}); + +const getSuggestionItems = ({ query }: { query: string }) => { + return [ + { + title: "Heading 1", + description: "Big section heading.", + icon: , + command: ({ editor, range }: Command) => { + editor + .chain() + .focus() + .deleteRange(range) + .setNode("heading", { level: 1 }) + .run(); + }, + }, + { + title: "Heading 2", + description: "Medium section heading.", + icon: , + command: ({ editor, range }: Command) => { + editor + .chain() + .focus() + .deleteRange(range) + .setNode("heading", { level: 2 }) + .run(); + }, + }, + { + title: "Heading 3", + description: "Small section heading.", + icon: , + command: ({ editor, range }: Command) => { + editor + .chain() + .focus() + .deleteRange(range) + .setNode("heading", { level: 3 }) + .run(); + }, + }, + { + title: "Bold", + description: "Make text bold.", + icon: , + command: ({ editor, range }: Command) => { + editor.chain().focus().deleteRange(range).setMark("bold").run(); + }, + }, + { + title: "Italic", + description: "Make text italic.", + icon: , + command: ({ editor, range }: Command) => { + editor.chain().focus().deleteRange(range).setMark("italic").run(); + }, + }, + { + title: "Strikethrough", + description: "Make text strikethrough.", + icon: , + command: ({ editor, range }: Command) => { + editor.chain().focus().deleteRange(range).setMark("strike").run(); + }, + }, + { + title: "Bullet List", + description: "Create a bullet list.", + icon: , + command: ({ editor, range }: Command) => { + editor.chain().focus().deleteRange(range).toggleBulletList().run(); + }, + }, + { + title: "Numbered List", + description: "Create a numbered list.", + icon: , + command: ({ editor, range }: Command) => { + editor.chain().focus().deleteRange(range).toggleOrderedList().run(); + }, + }, + { + title: "Code Block", + description: "Create a code block.", + icon: , + command: ({ editor, range }: Command) => { + editor.chain().focus().deleteRange(range).toggleCodeBlock().run(); + }, + }, + { + title: "Quote", + description: "Create a quote.", + icon: , + command: ({ editor, range }: Command) => { + editor.chain().focus().deleteRange(range).toggleBlockquote().run(); + }, + }, + { + title: "Align Left", + description: "Align text to the left.", + icon: , + command: ({ editor, range }: Command) => { + editor.chain().focus().deleteRange(range).setTextAlign("left").run(); + }, + }, + { + title: "Align Center", + description: "Align text to the center.", + icon: , + command: ({ editor, range }: Command) => { + editor.chain().focus().deleteRange(range).setTextAlign("center").run(); + }, + }, + { + title: "Align Right", + description: "Align text to the right.", + icon: , + command: ({ editor, range }: Command) => { + editor.chain().focus().deleteRange(range).setTextAlign("right").run(); + }, + }, + ].filter((item) => { + if (query.length > 0) { + return item.title.toLowerCase().includes(query.toLowerCase()); + } + return true; + }); + // .slice(0, 10); +}; + +export const updateScrollView = (container: HTMLElement, item: HTMLElement) => { + const containerHeight = container.offsetHeight; + const itemHeight = item.offsetHeight; + + const top = item.offsetTop; + const bottom = top + itemHeight; + + if (top < container.scrollTop) { + container.scrollTop -= container.scrollTop - top + 5; + } else if (bottom > containerHeight + container.scrollTop) { + container.scrollTop += bottom - containerHeight - container.scrollTop + 5; + } +}; + +const CommandList = ({ + items, + command, + editor, +}: { items: CommandItemProps[]; command: any; editor: any; range: any }) => { + const [selectedIndex, setSelectedIndex] = useState(0); + + const selectItem = useCallback( + (index: number) => { + const item = items[index]; + + command(item); + }, + [command, editor, items], + ); + + useEffect(() => { + const navigationKeys = ["ArrowUp", "ArrowDown", "Enter"]; + const onKeyDown = (e: KeyboardEvent) => { + if (navigationKeys.includes(e.key)) { + e.preventDefault(); + if (e.key === "ArrowUp") { + setSelectedIndex((selectedIndex + items.length - 1) % items.length); + return true; + } + if (e.key === "ArrowDown") { + setSelectedIndex((selectedIndex + 1) % items.length); + return true; + } + + if (e.key === "Enter") { + selectItem(selectedIndex); + return true; + } + return false; + } + }; + document.addEventListener("keydown", onKeyDown); + return () => { + document.removeEventListener("keydown", onKeyDown); + }; + }, [items, selectedIndex, setSelectedIndex, selectItem]); + + useEffect(() => { + setSelectedIndex(0); + }, [items]); + + const commandListContainer = useRef(null); + + useLayoutEffect(() => { + const container = commandListContainer.current; + + const item = container?.children[selectedIndex] as HTMLElement; + + if (container) { + updateScrollView(container, item); + } + }, [selectedIndex]); + + return items.length > 0 ? ( +
    + {items.map((item: CommandItemProps, index: number) => { + return ( + + ); + })} +
    + ) : null; +}; + +const renderItems = () => { + let component: ReactRenderer | null = null; + let popup: any; + + return { + onStart: (props: { editor: Editor; clientRect: DOMRect }) => { + component = new ReactRenderer(CommandList, { + props, + editor: props.editor, + }); + + // @ts-ignore + popup = tippy("body", { + getReferenceClientRect: props.clientRect, + appendTo: () => document.body, + content: component.element, + showOnCreate: true, + interactive: true, + trigger: "manual", + placement: "bottom-start", + }); + }, + onUpdate: (props: { editor: Editor; clientRect: DOMRect }) => { + component?.updateProps(props); + + popup?.[0].setProps({ + getReferenceClientRect: props.clientRect, + }); + }, + onKeyDown: (props: { event: KeyboardEvent }) => { + if (props.event.key === "Escape") { + popup?.[0].hide(); + + return true; + } + + // @ts-ignore + return component?.ref?.onKeyDown(props); + }, + onExit: () => { + popup?.[0].destroy(); + component?.destroy(); + }, + }; +}; + +const Slash = Command.configure({ + suggestion: { + items: getSuggestionItems, + render: renderItems, + }, +}); + +export default Slash; diff --git a/packages/dashboard/src/components/Input/MarkdownEditor/index.tsx b/packages/dashboard/src/components/Input/MarkdownEditor/index.tsx new file mode 100644 index 0000000..25794b0 --- /dev/null +++ b/packages/dashboard/src/components/Input/MarkdownEditor/index.tsx @@ -0,0 +1 @@ +export {default as Editor} from './Editor'; diff --git a/packages/dashboard/src/components/Input/MultiselectDropdown/MultiselectDropdown.tsx b/packages/dashboard/src/components/Input/MultiselectDropdown/MultiselectDropdown.tsx new file mode 100644 index 0000000..583f1fd --- /dev/null +++ b/packages/dashboard/src/components/Input/MultiselectDropdown/MultiselectDropdown.tsx @@ -0,0 +1,181 @@ +import React, {MutableRefObject, useEffect, useState} from 'react'; +import {AnimatePresence, motion} from 'framer-motion'; + +export interface MultiselectDropdownProps { + onChange: (value: string[]) => void; + values: readonly { + name: string; + value: string; + tag?: string; + }[]; + selectedValues?: readonly string[]; + disabled?: boolean; + className?: string; +} + +/** + * @param root0 + * @param root0.onChange + * @param root0.values + * @param root0.selectedValues + * @param root0.className + * @param root0.disabled + */ +export default function MultiselectDropdown({ + onChange, + values, + selectedValues: PropsselectedValues, + className, + disabled = false, +}: MultiselectDropdownProps) { + const [open, setOpen] = useState(false); + const [query, setQuery] = useState(''); + const [selectedValues, setSelectedValues] = useState([]); + + const ref = React.createRef(); + + useEffect(() => { + if (PropsselectedValues) { + setSelectedValues(PropsselectedValues); + } + }, [PropsselectedValues]); + + useEffect(() => { + const mutableRef = ref as MutableRefObject; + + const handleClickOutside = (event: any) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + if (mutableRef.current && !mutableRef.current.contains(event.target) && open) { + setOpen(!open); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [ref]); + + return ( + <> +
    +
    + + + + {open && ( + +
  • + setQuery(e.target.value)} + /> +
  • + + {values.filter(value => value.name.toLowerCase().includes(query.toLowerCase())).length === 0 ? ( +
  • + No results found +
  • + ) : ( + values + .filter(value => value.name.toLowerCase().includes(query.toLowerCase())) + .map((value, index) => { + return ( +
  • { + const isAlreadySelected = selectedValues.find(selection => value.value === selection); + + const updatedArray = isAlreadySelected + ? selectedValues.filter(selection => selection !== value.value) + : [...selectedValues, value.value]; + + onChange(updatedArray); + setSelectedValues(updatedArray); + }} + > + {value.tag && ( + + {value.tag} + + )} + + {value.name.charAt(0).toUpperCase() + value.name.slice(1).toLowerCase()} + + {value.value === selectedValues.find(selection => value.value === selection) ? ( + + + + ) : null} +
  • + ); + }) + )} +
    + )} +
    +
    +
    + + ); +} diff --git a/packages/dashboard/src/components/Input/MultiselectDropdown/index.tsx b/packages/dashboard/src/components/Input/MultiselectDropdown/index.tsx new file mode 100644 index 0000000..3cb4354 --- /dev/null +++ b/packages/dashboard/src/components/Input/MultiselectDropdown/index.tsx @@ -0,0 +1 @@ +export {default as MultiselectDropdown} from './MultiselectDropdown'; diff --git a/packages/dashboard/src/components/Input/Toggle/Toggle.tsx b/packages/dashboard/src/components/Input/Toggle/Toggle.tsx new file mode 100644 index 0000000..24aea9e --- /dev/null +++ b/packages/dashboard/src/components/Input/Toggle/Toggle.tsx @@ -0,0 +1,54 @@ +export interface ToggleProps { + title: string; + description: string; + toggled: boolean; + onToggle: () => void; + disabled?: boolean; + className?: string; +} + +/** + * @param root0 + * @param root0.toggled + * @param root0.onToggle + * @param root0.title + * @param root0.description + * @param root0.className + * @param root0.disabled + */ +export default function Toggle({title, description, toggled, onToggle, disabled, className}: ToggleProps) { + return ( + <> +
    + + + {title} + + {description} + + +
    + + ); +} diff --git a/packages/dashboard/src/components/Input/Toggle/index.tsx b/packages/dashboard/src/components/Input/Toggle/index.tsx new file mode 100644 index 0000000..08f7e1e --- /dev/null +++ b/packages/dashboard/src/components/Input/Toggle/index.tsx @@ -0,0 +1 @@ +export {default as Toggle} from './Toggle'; diff --git a/packages/dashboard/src/components/Input/index.tsx b/packages/dashboard/src/components/Input/index.tsx new file mode 100644 index 0000000..2db7c65 --- /dev/null +++ b/packages/dashboard/src/components/Input/index.tsx @@ -0,0 +1,5 @@ +export * from './Toggle'; +export * from './Dropdown'; +export * from './MultiselectDropdown'; +export * from './MarkdownEditor'; +export * from './Input'; diff --git a/packages/dashboard/src/components/Navigation/AnalyticsTabs/AnalyticsTabs.tsx b/packages/dashboard/src/components/Navigation/AnalyticsTabs/AnalyticsTabs.tsx new file mode 100644 index 0000000..1b0470c --- /dev/null +++ b/packages/dashboard/src/components/Navigation/AnalyticsTabs/AnalyticsTabs.tsx @@ -0,0 +1,19 @@ +import {Tabs} from '../Tabs'; +import React from 'react'; +import {useRouter} from 'next/router'; + +/** + * + * @param root0 + * @param root0.onMethodChange + */ +export default function AnalyticsTabs() { + const router = useRouter(); + + const links = [ + {to: '/analytics', text: 'Overview', active: router.route === '/analytics'}, + {to: '/analytics/clicks', text: 'Clicks', active: router.route === '/analytics/clicks'}, + ]; + + return ; +} diff --git a/packages/dashboard/src/components/Navigation/AnalyticsTabs/index.tsx b/packages/dashboard/src/components/Navigation/AnalyticsTabs/index.tsx new file mode 100644 index 0000000..f1b215f --- /dev/null +++ b/packages/dashboard/src/components/Navigation/AnalyticsTabs/index.tsx @@ -0,0 +1 @@ +export {default as AnalyticsTabs} from './AnalyticsTabs'; diff --git a/packages/dashboard/src/components/Navigation/DeveloperTabs/DeveloperTabs.tsx b/packages/dashboard/src/components/Navigation/DeveloperTabs/DeveloperTabs.tsx new file mode 100644 index 0000000..3a9f602 --- /dev/null +++ b/packages/dashboard/src/components/Navigation/DeveloperTabs/DeveloperTabs.tsx @@ -0,0 +1,14 @@ +import {Tabs} from '../Tabs'; +import React from 'react'; +import {useRouter} from 'next/router'; + +/** + * + */ +export default function DeveloperTabs() { + const router = useRouter(); + + const links = [{to: '/developers/webhooks', text: 'Webhooks', active: router.route === '/developers/webhooks'}]; + + return ; +} diff --git a/packages/dashboard/src/components/Navigation/DeveloperTabs/index.tsx b/packages/dashboard/src/components/Navigation/DeveloperTabs/index.tsx new file mode 100644 index 0000000..d0256db --- /dev/null +++ b/packages/dashboard/src/components/Navigation/DeveloperTabs/index.tsx @@ -0,0 +1 @@ +export {default as DeveloperTabs} from './DeveloperTabs'; diff --git a/packages/dashboard/src/components/Navigation/ProjectSelector/ProjectSelector.tsx b/packages/dashboard/src/components/Navigation/ProjectSelector/ProjectSelector.tsx new file mode 100644 index 0000000..41c78fb --- /dev/null +++ b/packages/dashboard/src/components/Navigation/ProjectSelector/ProjectSelector.tsx @@ -0,0 +1,159 @@ +import React, {MutableRefObject, useEffect} from 'react'; +import {AnimatePresence, motion} from 'framer-motion'; +import {useActiveProject, useProjects} from '../../../lib/hooks/projects'; +import {useRouter} from 'next/router'; +import {useAtom} from 'jotai'; +import {atomActiveProject} from '../../../lib/atoms/project'; + +export interface ProjectSelectorProps { + open: boolean; + onToggle: () => void; +} + +const ProjectSelector = React.forwardRef( + ({open, onToggle}: ProjectSelectorProps, ref) => { + const router = useRouter(); + const {data: projects} = useProjects(); + const activeProject = useActiveProject(); + const [, setActiveProjectId] = useAtom(atomActiveProject); + + useEffect(() => { + const mutableRef = ref as MutableRefObject; + + const handleClickOutside = (event: any) => { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + if (mutableRef.current && !mutableRef.current.contains(event.target) && open) { + onToggle(); + } + }; + + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [ref]); + + const onChange = (project: string) => { + localStorage.setItem('project', project); + setActiveProjectId(project); + window.location.href = '/'; + }; + + return ( + <> + +
    +
    + + + + {open && ( + +
    + {projects?.map((project, index) => { + return ( +
  • { + onChange(project.id); + onToggle(); + }} + > + + {project.name} + + {project.id === activeProject?.id ? ( + + + + ) : null} +
  • + ); + })} + +
    +
  • { + await router.push('/new'); + }} + > + Create new project +
  • +
    +
    + )} +
    +
    +
    + + ); + }, +); + +export default ProjectSelector; diff --git a/packages/dashboard/src/components/Navigation/ProjectSelector/index.tsx b/packages/dashboard/src/components/Navigation/ProjectSelector/index.tsx new file mode 100644 index 0000000..3461301 --- /dev/null +++ b/packages/dashboard/src/components/Navigation/ProjectSelector/index.tsx @@ -0,0 +1 @@ +export {default as ProjectSelector} from './ProjectSelector'; diff --git a/packages/dashboard/src/components/Navigation/SettingTabs/SettingTabs.tsx b/packages/dashboard/src/components/Navigation/SettingTabs/SettingTabs.tsx new file mode 100644 index 0000000..44cec78 --- /dev/null +++ b/packages/dashboard/src/components/Navigation/SettingTabs/SettingTabs.tsx @@ -0,0 +1,19 @@ +import {Tabs} from '../Tabs'; +import React from 'react'; +import {useRouter} from 'next/router'; + +/** + * + */ +export default function SettingTabs() { + const router = useRouter(); + + const links = [ + {to: '/settings/project', text: 'Project Settings', active: router.route === '/settings/project'}, + {to: '/settings/api', text: 'API Keys', active: router.route === '/settings/api'}, + {to: '/settings/identity', text: 'Verified Domain', active: router.route === '/settings/identity'}, + {to: '/settings/members', text: 'Members', active: router.route === '/settings/members'}, + ]; + + return ; +} diff --git a/packages/dashboard/src/components/Navigation/SettingTabs/index.tsx b/packages/dashboard/src/components/Navigation/SettingTabs/index.tsx new file mode 100644 index 0000000..807e6a0 --- /dev/null +++ b/packages/dashboard/src/components/Navigation/SettingTabs/index.tsx @@ -0,0 +1 @@ +export {default as SettingTabs} from './SettingTabs'; diff --git a/packages/dashboard/src/components/Navigation/Sidebar/Sidebar.tsx b/packages/dashboard/src/components/Navigation/Sidebar/Sidebar.tsx new file mode 100644 index 0000000..fe22f22 --- /dev/null +++ b/packages/dashboard/src/components/Navigation/Sidebar/Sidebar.tsx @@ -0,0 +1,366 @@ +import React, {ReactElement, useState} from 'react'; +import {useRouter} from 'next/router'; +import {AnimatePresence, motion} from 'framer-motion'; +import Link from 'next/link'; +import {ProjectSelector} from '../../index'; +import Image from 'next/image'; +import logo from '../../../../public/assets/logo.png'; +import {Home, LayoutTemplate, LineChart, LogOut, Send, Settings, TerminalSquare, Users2, Workflow} from 'lucide-react'; + +interface SidebarLinkType { + to: string; + text: string; + disabled: boolean; + highlight?: boolean; + position: 'top' | 'bottom'; + icon: ReactElement; +} + +interface SidebarLinkProps { + active?: boolean; + to: string; + text: string; + disabled?: boolean; + highlight?: boolean; + svgPath: React.ReactElement; +} + +export interface SidebarProps { + mobileOpen: boolean; + onSidebarVisibilityChange: () => void; +} + +const links: SidebarLinkType[] = [ + { + to: '/', + text: 'Dashboard', + disabled: false, + position: 'top', + icon: , + }, + { + to: '/contacts', + text: 'Contacts', + disabled: false, + position: 'top', + icon: , + }, + { + to: '/analytics', + text: 'Analytics', + disabled: false, + position: 'top', + icon: , + }, + // { + // to: '/developers', + // text: 'Developers', + // disabled: false, + // position: 'top', + // icon: , + // }, + { + to: '/settings/project', + text: 'Project Settings', + disabled: false, + position: 'top', + icon: , + }, + { + to: '/events', + text: 'Events', + disabled: false, + position: 'top', + icon: , + }, + { + to: '/templates', + text: 'Templates', + disabled: false, + position: 'top', + icon: , + }, + { + to: '/actions', + text: 'Actions', + disabled: false, + position: 'top', + icon: , + }, + { + to: '/campaigns', + text: 'Campaigns', + disabled: false, + position: 'top', + icon: , + }, + + // { + // to: '/settings/account', + // text: 'Account Settings', + // disabled: false, + // position: 'bottom', + // icon: ( + // <> + // + // + // ), + // }, +]; + +/** + * @param root0 + * @param root0.active + * @param root0.to + * @param root0.text + * @param root0.disabled + * @param root0.svgPath + * @param root0.highlight + */ +function SidebarLink({active, to, text, disabled, highlight, svgPath}: SidebarLinkProps) { + if (to.startsWith('http')) { + return ( + window.open(to, '_blank')?.focus()} + className={`${ + active + ? 'cursor-default bg-neutral-100 text-neutral-700' + : disabled + ? 'text-neutral-200' + : 'cursor-pointer text-neutral-400 hover:bg-neutral-50 hover:text-neutral-700' + } flex items-center gap-x-3 rounded p-2 text-sm font-medium transition ease-in-out`} + > +
    {svgPath}
    + {text} + {highlight &&
    New
    } +
    + ); + } + + return ( + +
    {svgPath}
    + {text} + {highlight &&
    New
    } + + ); +} + +/** + * @param root0 + * @param root0.mobileOpen + * @param root0.onSidebarVisibilityChange + */ +export default function Sidebar({mobileOpen, onSidebarVisibilityChange}: SidebarProps) { + const router = useRouter(); + + const projectSelectorRef = React.createRef(); + + const [projectSelectorOpen, setProjectSelectorOpen] = useState(false); + + return ( + <> + + {mobileOpen && ( + + + +
    +
    + +
    + +
    +
    + + Logo + +
    + +
    + setProjectSelectorOpen(!projectSelectorOpen)} + ref={projectSelectorRef} + /> +
    + + +
    + +
    + +
    +
    + +
    + + )} + + + {/* Static sidebar for desktop */} +
    +
    +
    +
    +
    + + Logo + +
    + +
    + setProjectSelectorOpen(!projectSelectorOpen)} + ref={projectSelectorRef} + /> +
    + + +
    + +
    + +
    + +
    + Sign out + +
    +
    +
    +
    + + ); +} diff --git a/packages/dashboard/src/components/Navigation/Sidebar/index.tsx b/packages/dashboard/src/components/Navigation/Sidebar/index.tsx new file mode 100644 index 0000000..9a21f7b --- /dev/null +++ b/packages/dashboard/src/components/Navigation/Sidebar/index.tsx @@ -0,0 +1 @@ +export {default as Sidebar} from './Sidebar'; diff --git a/packages/dashboard/src/components/Navigation/Tabs/Tabs.tsx b/packages/dashboard/src/components/Navigation/Tabs/Tabs.tsx new file mode 100644 index 0000000..6a7a67c --- /dev/null +++ b/packages/dashboard/src/components/Navigation/Tabs/Tabs.tsx @@ -0,0 +1,61 @@ +import Link from 'next/link'; +import {useRouter} from 'next/router'; + +export interface TabProps { + links: { + to: string; + text: string; + active: boolean; + }[]; +} + +/** + * @param root0 + * @param root0.links + */ +export default function Tabs({links}: TabProps) { + const router = useRouter(); + return ( +
    +
    + + +
    +
    +
    + +
    +
    +
    + ); +} diff --git a/packages/dashboard/src/components/Navigation/Tabs/index.tsx b/packages/dashboard/src/components/Navigation/Tabs/index.tsx new file mode 100644 index 0000000..ad34677 --- /dev/null +++ b/packages/dashboard/src/components/Navigation/Tabs/index.tsx @@ -0,0 +1 @@ +export {default as Tabs} from './Tabs'; diff --git a/packages/dashboard/src/components/Navigation/index.tsx b/packages/dashboard/src/components/Navigation/index.tsx new file mode 100644 index 0000000..08eb58c --- /dev/null +++ b/packages/dashboard/src/components/Navigation/index.tsx @@ -0,0 +1,6 @@ +export * from './Sidebar'; +export * from './ProjectSelector'; +export * from './Tabs'; +export * from './SettingTabs'; +export * from './AnalyticsTabs'; +export * from './DeveloperTabs'; diff --git a/packages/dashboard/src/components/Overlay/Modal/Modal.tsx b/packages/dashboard/src/components/Overlay/Modal/Modal.tsx new file mode 100644 index 0000000..6640092 --- /dev/null +++ b/packages/dashboard/src/components/Overlay/Modal/Modal.tsx @@ -0,0 +1,181 @@ +import React from 'react'; +import {AnimatePresence, motion} from 'framer-motion'; + +export interface ModalProps { + title: string; + description?: string; + isOpen: boolean; + onToggle: () => void; + onAction: () => void; + children?: React.ReactNode; + action?: string; + type: 'info' | 'danger'; + icon?: React.ReactNode; +} + +/** + * @param root0 + * @param root0.isOpen + * @param root0.onToggle + * @param root0.onAction + * @param root0.children + * @param root0.action + * @param root0.type + * @param root0.title + * @param root0.description + * @param root0.icon + */ +export default function Modal({ + title, + description, + isOpen, + onToggle, + onAction, + children, + action, + type, + icon, +}: ModalProps) { + return ( + + {isOpen && ( +
    +
    +
    +
    + )} +
    + ); +} diff --git a/packages/dashboard/src/components/Overlay/Modal/index.tsx b/packages/dashboard/src/components/Overlay/Modal/index.tsx new file mode 100644 index 0000000..1693a3e --- /dev/null +++ b/packages/dashboard/src/components/Overlay/Modal/index.tsx @@ -0,0 +1 @@ +export {default as Modal} from './Modal'; diff --git a/packages/dashboard/src/components/Overlay/index.tsx b/packages/dashboard/src/components/Overlay/index.tsx new file mode 100644 index 0000000..25f42fe --- /dev/null +++ b/packages/dashboard/src/components/Overlay/index.tsx @@ -0,0 +1 @@ +export * from './Modal/index'; diff --git a/packages/dashboard/src/components/Skeleton/Skeleton.tsx b/packages/dashboard/src/components/Skeleton/Skeleton.tsx new file mode 100644 index 0000000..ab1dbf7 --- /dev/null +++ b/packages/dashboard/src/components/Skeleton/Skeleton.tsx @@ -0,0 +1,81 @@ +export interface SkeletonProps { + type: 'table' | 'card'; +} + +/** + * + * @param root0 + * @param root0.type + */ +export default function Skeleton({type}: SkeletonProps) { + if (type === 'table') { + return ( + <> +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    + Loading... +
    + + ); + } + + return <>; +} diff --git a/packages/dashboard/src/components/Skeleton/index.tsx b/packages/dashboard/src/components/Skeleton/index.tsx new file mode 100644 index 0000000..b88c48f --- /dev/null +++ b/packages/dashboard/src/components/Skeleton/index.tsx @@ -0,0 +1 @@ +export {default as Skeleton} from './Skeleton'; diff --git a/packages/dashboard/src/components/Table/Table.tsx b/packages/dashboard/src/components/Table/Table.tsx new file mode 100644 index 0000000..87baf28 --- /dev/null +++ b/packages/dashboard/src/components/Table/Table.tsx @@ -0,0 +1,110 @@ +import React from 'react'; + +export interface TableProps { + values: { + // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents + [key: string]: string | number | boolean | Date | React.ReactNode | null; + }[]; +} + +/** + * @param root0 + * @param root0.values + */ +export default function Table({values}: TableProps) { + if (values.length === 0) { + return

    No values provided

    ; + } + + return ( +
    +
    +
    +
    + + + + {Object.keys(values[0]).map(header => { + return ( + + ); + })} + + + + {values.map(row => { + return ( + + {Object.entries(row).map(value => { + if (value[1] === null || value[1] === undefined) { + return ( + + ); + } + + if (typeof value[1] === 'boolean') { + return ( + + ); + } + + // @ts-ignore + return ; + })} + + ); + })} + +
    + {header} +
    Not specified + {value[1] ? ( + + + + ) : ( + + + + + )} + {value[1]}
    +
    +
    +
    +
    + ); +} diff --git a/packages/dashboard/src/components/Table/index.tsx b/packages/dashboard/src/components/Table/index.tsx new file mode 100644 index 0000000..8e64957 --- /dev/null +++ b/packages/dashboard/src/components/Table/index.tsx @@ -0,0 +1 @@ +export {default as Table} from './Table'; diff --git a/packages/dashboard/src/components/Utility/Empty/Empty.tsx b/packages/dashboard/src/components/Utility/Empty/Empty.tsx new file mode 100644 index 0000000..2d52b76 --- /dev/null +++ b/packages/dashboard/src/components/Utility/Empty/Empty.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import {Ghost} from 'lucide-react'; + +export interface EmptyProps { + title: string; + description: string; + icon?: React.ReactNode; +} + +/** + * @param root0 + * @param root0.title + * @param root0.description + * @param root0.icon + */ +export default function Empty({title, description, icon}: EmptyProps) { + return ( +
    + + {title} + {description} +
    + ); +} diff --git a/packages/dashboard/src/components/Utility/Empty/index.tsx b/packages/dashboard/src/components/Utility/Empty/index.tsx new file mode 100644 index 0000000..940f4de --- /dev/null +++ b/packages/dashboard/src/components/Utility/Empty/index.tsx @@ -0,0 +1 @@ +export {default as Empty} from './Empty'; diff --git a/packages/dashboard/src/components/Utility/FullscreenLoader/FullscreenLoader.tsx b/packages/dashboard/src/components/Utility/FullscreenLoader/FullscreenLoader.tsx new file mode 100644 index 0000000..f2c054e --- /dev/null +++ b/packages/dashboard/src/components/Utility/FullscreenLoader/FullscreenLoader.tsx @@ -0,0 +1,26 @@ +/** + * + */ +import { LineWobble } from "@uiball/loaders"; + +/** + * + */ +export default function FullscreenLoader() { + return ( +
    +
    +

    + Loading... +

    +

    + Does this take longer than expected? Try clearing your browser's cache + or check if you have an ad blocker enabled! +

    +
    + +
    +
    +
    + ); +} diff --git a/packages/dashboard/src/components/Utility/FullscreenLoader/index.tsx b/packages/dashboard/src/components/Utility/FullscreenLoader/index.tsx new file mode 100644 index 0000000..436728b --- /dev/null +++ b/packages/dashboard/src/components/Utility/FullscreenLoader/index.tsx @@ -0,0 +1 @@ +export {default as FullscreenLoader} from './FullscreenLoader'; diff --git a/packages/dashboard/src/components/Utility/ProgressBar/ProgressBar.tsx b/packages/dashboard/src/components/Utility/ProgressBar/ProgressBar.tsx new file mode 100644 index 0000000..3c3d7f4 --- /dev/null +++ b/packages/dashboard/src/components/Utility/ProgressBar/ProgressBar.tsx @@ -0,0 +1,24 @@ +import {motion} from 'framer-motion'; +import React from 'react'; + +export interface ProgressBarProps { + percentage: number; +} + +/** + * @param root0 + * @param root0.percentage + */ +export default function ProgressBar({percentage}: ProgressBarProps) { + const formattedPercentage = isNaN(percentage) ? 0 : percentage; + + return ( +
    + +
    + ); +} diff --git a/packages/dashboard/src/components/Utility/ProgressBar/index.tsx b/packages/dashboard/src/components/Utility/ProgressBar/index.tsx new file mode 100644 index 0000000..ce72bbe --- /dev/null +++ b/packages/dashboard/src/components/Utility/ProgressBar/index.tsx @@ -0,0 +1 @@ +export {default as ProgressBar} from './ProgressBar'; diff --git a/packages/dashboard/src/components/Utility/Redirect/Redirect.tsx b/packages/dashboard/src/components/Utility/Redirect/Redirect.tsx new file mode 100644 index 0000000..12b18a2 --- /dev/null +++ b/packages/dashboard/src/components/Utility/Redirect/Redirect.tsx @@ -0,0 +1,20 @@ +import {useEffect} from 'react'; +import {useRouter} from 'next/router'; + +export interface RedirectProps { + to: string; +} + +/** + * @param root0 + * @param root0.to + */ +export default function Redirect({to}: RedirectProps) { + const router = useRouter(); + + useEffect(() => { + void router.push(to); + }, []); + + return null; +} diff --git a/packages/dashboard/src/components/Utility/Redirect/index.tsx b/packages/dashboard/src/components/Utility/Redirect/index.tsx new file mode 100644 index 0000000..cef991e --- /dev/null +++ b/packages/dashboard/src/components/Utility/Redirect/index.tsx @@ -0,0 +1 @@ +export {default as Redirect} from './Redirect'; diff --git a/packages/dashboard/src/components/Utility/Tooltip/Tooltip.tsx b/packages/dashboard/src/components/Utility/Tooltip/Tooltip.tsx new file mode 100644 index 0000000..b2f5e3d --- /dev/null +++ b/packages/dashboard/src/components/Utility/Tooltip/Tooltip.tsx @@ -0,0 +1,37 @@ +import Tippy from '@tippyjs/react'; +import React, {ReactNode} from 'react'; + +export interface TooltipProps { + content: ReactNode | string; + icon: ReactNode; +} + +/** + * + * @param root0 + * @param root0.content + * @param root0.icon + */ +export default function Tooltip({content, icon}: TooltipProps) { + return ( + <> + {content}
    } + > + + {icon} + + + + ); +} diff --git a/packages/dashboard/src/components/Utility/Tooltip/index.tsx b/packages/dashboard/src/components/Utility/Tooltip/index.tsx new file mode 100644 index 0000000..82dd46d --- /dev/null +++ b/packages/dashboard/src/components/Utility/Tooltip/index.tsx @@ -0,0 +1 @@ +export {default as Tooltip} from './Tooltip'; diff --git a/packages/dashboard/src/components/Utility/index.tsx b/packages/dashboard/src/components/Utility/index.tsx new file mode 100644 index 0000000..d44b159 --- /dev/null +++ b/packages/dashboard/src/components/Utility/index.tsx @@ -0,0 +1,5 @@ +export * from './Redirect'; +export * from './FullscreenLoader'; +export * from './Empty'; +export * from './ProgressBar'; +export * from './Tooltip'; diff --git a/packages/dashboard/src/components/index.ts b/packages/dashboard/src/components/index.ts new file mode 100644 index 0000000..cbaeeca --- /dev/null +++ b/packages/dashboard/src/components/index.ts @@ -0,0 +1,10 @@ +export * from './Input'; +export * from './Utility'; +export * from './Alert'; +export * from './Badge'; +export * from './Table'; +export * from './Overlay'; +export * from './Navigation'; +export * from './Card'; +export * from './Skeleton'; +export * from './CodeBlock'; diff --git a/packages/dashboard/src/layouts/Dashboard.tsx b/packages/dashboard/src/layouts/Dashboard.tsx new file mode 100644 index 0000000..9bd2ff8 --- /dev/null +++ b/packages/dashboard/src/layouts/Dashboard.tsx @@ -0,0 +1,72 @@ +import React, {useState} from 'react'; +import {FullscreenLoader, Redirect, Sidebar} from '../components'; +import {useActiveProject, useProjects} from '../lib/hooks/projects'; +import {useUser} from '../lib/hooks/users'; +import {AnimatePresence, motion} from 'framer-motion'; +import {useRouter} from 'next/router'; + +export const Dashboard = (props: {children: React.ReactNode}) => { + const router = useRouter(); + const activeProject = useActiveProject(); + const {data: projects} = useProjects(); + const {data: user} = useUser(); + + const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false); + + if (!projects || !user || !activeProject) { + return ; + } + + if (projects.length === 0) { + return ; + } + + return ( + <> +
    + setMobileSidebarOpen(!mobileSidebarOpen)} + /> +
    +
    + +
    +
    +
    +
    + + + {props.children} + + +
    +
    +
    +
    +
    + + ); +}; diff --git a/packages/dashboard/src/layouts/index.ts b/packages/dashboard/src/layouts/index.ts new file mode 100644 index 0000000..19bd377 --- /dev/null +++ b/packages/dashboard/src/layouts/index.ts @@ -0,0 +1 @@ +export * from './Dashboard'; diff --git a/packages/dashboard/src/lib/atoms/project.ts b/packages/dashboard/src/lib/atoms/project.ts new file mode 100644 index 0000000..c102287 --- /dev/null +++ b/packages/dashboard/src/lib/atoms/project.ts @@ -0,0 +1,5 @@ +import {atom} from 'jotai'; + +export const atomActiveProject = atom( + typeof window !== 'undefined' ? window.localStorage.getItem('project') : null, +); diff --git a/packages/dashboard/src/lib/constants.ts b/packages/dashboard/src/lib/constants.ts new file mode 100644 index 0000000..f9b7b5b --- /dev/null +++ b/packages/dashboard/src/lib/constants.ts @@ -0,0 +1,4 @@ +export const API_URI = process.env.NEXT_PUBLIC_API_URI ?? 'http://localhost:8080'; +export const AWS_REGION = process.env.NEXT_PUBLIC_AWS_REGION; + +export const NO_AUTH_ROUTES = ['/auth/signup', '/auth/login', '/auth/reset', '/unsubscribe/[id]', '/subscribe/[id]']; diff --git a/packages/dashboard/src/lib/hooks/actions.ts b/packages/dashboard/src/lib/hooks/actions.ts new file mode 100644 index 0000000..70123c6 --- /dev/null +++ b/packages/dashboard/src/lib/hooks/actions.ts @@ -0,0 +1,43 @@ +import useSWR from 'swr'; +import {Action, Email, Event, Task, Template, Trigger} from '@prisma/client'; +import {useActiveProject} from './projects'; + +/** + * + * @param id + */ +export function useAction(id: string) { + return useSWR(`/v1/actions/${id}`); +} + +/** + * + * @param id + */ +export function useRelatedActions(id: string) { + return useSWR< + (Action & { + events: Event[]; + notevents: Event[]; + triggers: Trigger[]; + emails: Email[]; + template: Template; + })[] + >(`/v1/actions/${id}/related`); +} + +/** + * + */ +export function useActions() { + const activeProject = useActiveProject(); + + return useSWR< + (Action & { + triggers: Trigger[]; + template: Template; + emails: Email[]; + tasks: Task[]; + })[] + >(activeProject ? `/projects/id/${activeProject.id}/actions` : null); +} diff --git a/packages/dashboard/src/lib/hooks/analytics.ts b/packages/dashboard/src/lib/hooks/analytics.ts new file mode 100644 index 0000000..d4239a1 --- /dev/null +++ b/packages/dashboard/src/lib/hooks/analytics.ts @@ -0,0 +1,34 @@ +import {useActiveProject} from './projects'; +import useSWR from 'swr'; + +/** + + * @param method + */ +export function useAnalytics(method?: 'week' | 'month' | 'year') { + const activeProject = useActiveProject(); + + return useSWR<{ + contacts: { + timeseries: { + day: Date; + count: number; + }[]; + subscribed: number; + unsubscribed: number; + }; + emails: { + total: number; + bounced: number; + opened: number; + complaint: number; + totalPrev: number; + bouncedPrev: number; + openedPrev: number; + complaintPrev: number; + }; + clicks: { + actions: {link: string; name: string; count: number}[]; + }; + }>(activeProject ? `/projects/id/${activeProject.id}/analytics?method=${method ?? 'week'}` : null); +} diff --git a/packages/dashboard/src/lib/hooks/campaigns.ts b/packages/dashboard/src/lib/hooks/campaigns.ts new file mode 100644 index 0000000..0bd89ef --- /dev/null +++ b/packages/dashboard/src/lib/hooks/campaigns.ts @@ -0,0 +1,33 @@ +import useSWR from 'swr'; +import {Campaign} from '@prisma/client'; +import {useActiveProject} from './projects'; + +/** + * + * @param id + */ +export function useCampaign(id: string) { + return useSWR(`/v1/campaigns/${id}`); +} + +/** + * + */ +export function useCampaigns() { + const activeProject = useActiveProject(); + + return useSWR< + (Campaign & { + emails: { + id: string; + status: string; + }[]; + tasks: { + id: string; + }[]; + recipients: { + id: string; + }[]; + })[] + >(activeProject ? `/projects/id/${activeProject.id}/campaigns` : null); +} diff --git a/packages/dashboard/src/lib/hooks/contacts.ts b/packages/dashboard/src/lib/hooks/contacts.ts new file mode 100644 index 0000000..dba692d --- /dev/null +++ b/packages/dashboard/src/lib/hooks/contacts.ts @@ -0,0 +1,106 @@ +import useSWR from 'swr'; +import {Action, Contact, Email, Event, Project, Trigger} from '@prisma/client'; +import {useActiveProject} from './projects'; + +export interface WithProject { + id: string; + withProject: true; +} + +export interface WithoutProject { + id: string; + withProject?: false; +} + +export type WithOrWithoutProject = T extends WithProject + ? + | (Contact & { + emails: Email[]; + triggers: (Trigger & { + event: Event | null; + action: Action | null; + })[]; + project: Project; + }) + | null + : + | (Contact & { + emails: Email[]; + triggers: (Trigger & { + event: Event | null; + action: Action | null; + })[]; + }) + | null; + +/** + * + * @param id.id + * @param id + * @param id.withProject + */ +export function useContact({id, withProject = false}: T) { + return useSWR>(withProject ? `/v1/contacts/${id}?withProject=true` : `/v1/contacts/${id}`); +} + +/** + * + * @param page + */ +export function useContacts(page: number) { + const activeProject = useActiveProject(); + + return useSWR<{ + contacts: (Contact & { + triggers: Trigger[]; + })[]; + count: number; + }>(activeProject ? `/projects/id/${activeProject.id}/contacts?page=${page}` : null); +} + +/** + * + */ +export function useContactsCount() { + const activeProject = useActiveProject(); + + return useSWR(activeProject ? `/projects/id/${activeProject.id}/contacts/count` : null); +} + +/** + * + */ +export function useContactMetadata() { + const activeProject = useActiveProject(); + + return useSWR(activeProject ? `/projects/id/${activeProject.id}/contacts/metadata` : null); +} + +/** + * + * @param query + */ +export function searchContacts(query: string | undefined) { + const activeProject = useActiveProject(); + + if (!query) { + return useSWR<{ + contacts: (Contact & { + triggers: Trigger[]; + emails: Email[]; + })[]; + count: number; + }>(activeProject ? `/projects/id/${activeProject.id}/contacts` : null); + } + + return useSWR<{ + contacts: (Contact & { + triggers: Trigger[]; + emails: Email[]; + })[]; + count: number; + }>(activeProject ? `/projects/id/${activeProject.id}/contacts/search?query=${query}` : null, { + revalidateOnFocus: false, + refreshInterval: 0, + }); +} diff --git a/packages/dashboard/src/lib/hooks/emails.ts b/packages/dashboard/src/lib/hooks/emails.ts new file mode 100644 index 0000000..8eddba2 --- /dev/null +++ b/packages/dashboard/src/lib/hooks/emails.ts @@ -0,0 +1,21 @@ +import {useActiveProject} from './projects'; +import useSWR from 'swr'; +import {Email} from '@prisma/client'; + +/** + * + */ +export function useEmails() { + const activeProject = useActiveProject(); + + return useSWR(activeProject ? `/projects/id/${activeProject.id}/emails` : null); +} + +/** + * + */ +export function useEmailsCount() { + const activeProject = useActiveProject(); + + return useSWR(activeProject ? `/projects/id/${activeProject.id}/emails/count` : null); +} diff --git a/packages/dashboard/src/lib/hooks/events.ts b/packages/dashboard/src/lib/hooks/events.ts new file mode 100644 index 0000000..82a1139 --- /dev/null +++ b/packages/dashboard/src/lib/hooks/events.ts @@ -0,0 +1,29 @@ +import useSWR from 'swr'; +import {Event} from '@prisma/client'; +import {useActiveProject} from './projects'; + +/** + * + */ +export function useEvents() { + const activeProject = useActiveProject(); + + return useSWR< + (Event & { + triggers: { + id: string; + createdAt: Date; + contactId: string; + }[]; + })[] + >(activeProject ? `/projects/id/${activeProject.id}/events` : null); +} + +/** + * + */ +export function useEventsWithoutTriggers() { + const activeProject = useActiveProject(); + + return useSWR(activeProject ? `/projects/id/${activeProject.id}/events?triggers=false` : null); +} diff --git a/packages/dashboard/src/lib/hooks/projects.ts b/packages/dashboard/src/lib/hooks/projects.ts new file mode 100644 index 0000000..eb23c3a --- /dev/null +++ b/packages/dashboard/src/lib/hooks/projects.ts @@ -0,0 +1,82 @@ +import {Action, Contact, Email, Event, Project, Role} from '@prisma/client'; +import {useAtom} from 'jotai'; +import useSWR from 'swr'; +import {atomActiveProject} from '../atoms/project'; + +/** + * + */ +export function useProjects() { + return useSWR('/users/@me/projects'); +} + +/** + * + */ +export function useActiveProject(): Project | null { + const [activeProject, setActiveProject] = useAtom(atomActiveProject); + const {data: projects} = useProjects(); + + if (!projects) { + return null; + } + + if (activeProject && !projects.find(project => project.id === activeProject)) { + setActiveProject(null); + window.localStorage.removeItem('project'); + } + + if (!activeProject && projects.length > 0) { + setActiveProject(projects[0].id); + window.localStorage.setItem('project', projects[0].id); + } + + return projects.find(project => project.id === activeProject) ?? null; +} + +/** + * + */ +export function useActiveProjectMemberships() { + const activeProject = useActiveProject(); + + return useSWR< + { + userId: string; + email: string; + role: Role; + }[] + >(activeProject ? `/projects/id/${activeProject.id}/memberships` : null); +} + +/** + * + */ +export function useActiveProjectFeed(page: number) { + const activeProject = useActiveProject(); + + return useSWR< + ( + | { + createdAt: Date; + contact: Contact; + event: Event | null; + action: Action | null; + } + | ({ + contact: Contact; + } & Email) + )[] + >(activeProject ? `/projects/id/${activeProject.id}/feed?page=${page}` : null); +} + +/** + * + */ +export function useActiveProjectVerifiedIdentity() { + const activeProject = useActiveProject(); + + return useSWR<{ + tokens: string[]; + }>(activeProject ? `/identities/id/${activeProject.id}` : null); +} diff --git a/packages/dashboard/src/lib/hooks/templates.ts b/packages/dashboard/src/lib/hooks/templates.ts new file mode 100644 index 0000000..a05dce5 --- /dev/null +++ b/packages/dashboard/src/lib/hooks/templates.ts @@ -0,0 +1,24 @@ +import useSWR from 'swr'; +import {Action, Template} from '@prisma/client'; +import {useActiveProject} from './projects'; + +/** + * + * @param id + */ +export function useTemplate(id: string) { + return useSWR(`/v1/templates/${id}`); +} + +/** + * + */ +export function useTemplates() { + const activeProject = useActiveProject(); + + return useSWR< + (Template & { + actions: Action[]; + })[] + >(activeProject ? `/projects/id/${activeProject.id}/templates` : null); +} diff --git a/packages/dashboard/src/lib/hooks/users.ts b/packages/dashboard/src/lib/hooks/users.ts new file mode 100644 index 0000000..6134253 --- /dev/null +++ b/packages/dashboard/src/lib/hooks/users.ts @@ -0,0 +1,9 @@ +import useSWR from 'swr'; + +/** + * Fetch the current user. undefined means loading, null means logged out + * + */ +export function useUser() { + return useSWR('/users/@me', {shouldRetryOnError: false}); +} diff --git a/packages/dashboard/src/lib/network.ts b/packages/dashboard/src/lib/network.ts new file mode 100644 index 0000000..11feac2 --- /dev/null +++ b/packages/dashboard/src/lib/network.ts @@ -0,0 +1,67 @@ +import {API_URI} from './constants'; +import {infer as ZodInfer, ZodSchema} from 'zod'; + +interface Json { + [x: string]: string | number | boolean | Date | Json | JsonArray; +} + +type JsonArray = (string | number | boolean | Date | Json | JsonArray)[]; + +interface TypedSchema extends ZodSchema { + _type: any; +} + +export class network { + /** + * Fetcher function that includes toast support + * @param method Request method + * @param path Request endpoint or path + * @param body Request body + */ + public static async fetch( + method: 'GET' | 'PUT' | 'POST' | 'DELETE', + path: string, + body?: Schema extends TypedSchema ? ZodInfer : never, + ): Promise { + const url = path.startsWith('http') ? path : API_URI + path; + const response = await fetch(url, { + method, + body: body && JSON.stringify(body), + headers: body && {'Content-Type': 'application/json'}, + credentials: 'include', + }); + + const res = await response.json(); + + if (response.status >= 400) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + throw new Error(res?.message ?? 'Something went wrong!'); + } + + return res; + } + + public static async mock( + key: string, + method: 'GET' | 'PUT' | 'POST' | 'DELETE', + path: string, + body?: Schema extends TypedSchema ? ZodInfer : never, + ): Promise { + const url = path.startsWith('http') ? path : API_URI + path; + const response = await fetch(url, { + method, + body: body && JSON.stringify(body), + headers: {'Content-Type': 'application/json', 'Authorization': `Bearer ${key}`}, + credentials: 'include', + }); + + const res = await response.json(); + + if (response.status >= 400) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + throw new Error(res?.message ?? 'Something went wrong!'); + } + + return res; + } +} diff --git a/packages/dashboard/src/pages/_app.tsx b/packages/dashboard/src/pages/_app.tsx new file mode 100644 index 0000000..7b31996 --- /dev/null +++ b/packages/dashboard/src/pages/_app.tsx @@ -0,0 +1,105 @@ +import "../../styles/index.css"; + +import dayjs from "dayjs"; +import utc from "dayjs/plugin/utc"; +import { Provider as JotaiProvider } from "jotai"; +import type { AppProps } from "next/app"; +import Head from "next/head"; +import Router, { useRouter } from "next/router"; +import NProgress from "nprogress"; +import React from "react"; +import { Toaster } from "sonner"; +import { SWRConfig } from "swr"; +import { network } from "../lib/network"; +import "nprogress/nprogress.css"; +import advancedFormat from "dayjs/plugin/advancedFormat"; +import duration from "dayjs/plugin/duration"; +import relativeTime from "dayjs/plugin/relativeTime"; +import { DefaultSeo } from "next-seo"; +import { FullscreenLoader, Redirect } from "../components"; +import { NO_AUTH_ROUTES } from "../lib/constants"; +import { useUser } from "../lib/hooks/users"; + +dayjs.extend(relativeTime); +dayjs.extend(utc); +dayjs.extend(advancedFormat); +dayjs.extend(duration); + +Router.events.on("routeChangeStart", () => NProgress.start()); +Router.events.on("routeChangeComplete", () => NProgress.done()); +Router.events.on("routeChangeError", () => NProgress.done()); + +/** + * Main app component + * @param props Props + * @param props.Component App component + * @param props.pageProps + */ +function App({ Component, pageProps }: AppProps) { + const router = useRouter(); + const { data: user, error } = useUser(); + + if (error && !NO_AUTH_ROUTES.includes(router.route)) { + return ; + } + + if (!user && !NO_AUTH_ROUTES.includes(router.route)) { + return ; + } + + return ( + <> + + Plunk Dashboard | The Email Platform for SaaS + + + + + + + ); +} + +/** + * Main app root component that houses all components + * @param props Default nextjs props + */ +export default function WithProviders(props: AppProps) { + return ( + network.fetch("GET", url), + revalidateOnFocus: true, + }} + > + + + + + + + ); +} diff --git a/packages/dashboard/src/pages/_document.tsx b/packages/dashboard/src/pages/_document.tsx new file mode 100644 index 0000000..bc0c794 --- /dev/null +++ b/packages/dashboard/src/pages/_document.tsx @@ -0,0 +1,37 @@ +import Document, {Head, Html, Main, NextScript} from 'next/document'; +import React from 'react'; + +export default class MyDocument extends Document { + public render() { + return ( + + + {/* Start fonts */} + + + + {/* End fonts */} + + {/* Start favicon */} + + + + + + + + + + {/* End favicon */} + + +
    + + + + ); + } +} diff --git a/packages/dashboard/src/pages/actions/[id].tsx b/packages/dashboard/src/pages/actions/[id].tsx new file mode 100644 index 0000000..873d78c --- /dev/null +++ b/packages/dashboard/src/pages/actions/[id].tsx @@ -0,0 +1,614 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { ActionSchemas, type UtilitySchemas } from "@plunk/shared"; +import type { Action } from "@prisma/client"; +import { useEvents } from "dashboard/src/lib/hooks/events"; +import { useTemplates } from "dashboard/src/lib/hooks/templates"; +import dayjs from "dayjs"; +import { AnimatePresence, motion } from "framer-motion"; +import { Save } from "lucide-react"; +import Link from "next/link"; +import { useRouter } from "next/router"; +import React, { useEffect, useState } from "react"; +import { type FieldError, useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { + Badge, + Card, + Dropdown, + Empty, + FullscreenLoader, + Input, + MultiselectDropdown, + Toggle, +} from "../../components"; +import { Dashboard } from "../../layouts"; +import { + useAction, + useActions, + useRelatedActions, +} from "../../lib/hooks/actions"; +import { useActiveProject } from "../../lib/hooks/projects"; +import { network } from "../../lib/network"; + +interface ActionValues { + name: string; + runOnce: boolean; + delay: number; + template: string; + events: string[]; + notevents: string[]; +} + +/** + * + */ +export default function Index() { + const router = useRouter(); + + if (!router.isReady) { + return ; + } + + const project = useActiveProject(); + const { mutate } = useActions(); + const { data: templates } = useTemplates(); + const { data: events } = useEvents(); + const { data: action } = useAction(router.query.id as string); + const { data: related } = useRelatedActions(router.query.id as string); + + const [delay, setDelay] = useState<{ + delay: number; + unit: "MINUTES" | "HOURS" | "DAYS"; + }>({ + delay: 0, + unit: "MINUTES", + }); + + useEffect(() => { + switch (delay.unit) { + case "MINUTES": + setValue("delay", delay.delay); + break; + case "HOURS": + setValue("delay", delay.delay * 60); + break; + case "DAYS": + setValue("delay", delay.delay * 24 * 60); + break; + } + }, [delay]); + + const { + register, + handleSubmit, + formState: { errors }, + watch, + reset, + setValue, + } = useForm({ + defaultValues: { events: [], notevents: [] }, + resolver: zodResolver(ActionSchemas.update), + }); + + useEffect(() => { + if (!action) { + return; + } + + if (action.delay !== 0) { + if (action.delay % 1440 === 0) { + setDelay({ unit: "DAYS", delay: action.delay / 1440 }); + } else if (action.delay % 60 === 0) { + setDelay({ unit: "HOURS", delay: action.delay / 60 }); + } else { + setDelay({ unit: "MINUTES", delay: action.delay }); + } + } + + reset({ + ...action, + template: action.templateId, + delay: 0, + events: action.events.map((e: { id: string }) => e.id), + notevents: action.notevents.map((e: { id: string }) => e.id), + }); + }, [reset, action]); + + if (!project || !action || !templates || !events || !related) { + return ; + } + + const updateAction = (data: ActionValues) => { + toast.promise( + network.mock( + project.secret, + "PUT", + "/v1/actions", + { + id: action.id, + ...data, + }, + ), + { + loading: "Saving your action", + success: () => { + void mutate(); + return "Saved your action"; + }, + error: "Could not save your action!", + }, + ); + }; + + const remove = async (e: { preventDefault: () => void }) => { + e.preventDefault(); + toast.promise( + network.mock( + project.secret, + "DELETE", + "/v1/actions", + { + id: action.id, + }, + ), + { + loading: "Deleting your action", + success: () => { + void mutate(); + return "Deleted your action"; + }, + error: "Could not delete your action!", + }, + ); + + await router.push("/actions"); + }; + + return ( + <> + + + + + } + > +
    + + +
    + + setValue("events", e)} + values={events + .filter( + (e) => !e.campaignId && !watch("notevents").includes(e.id), + ) + .sort((a, b) => { + if (a.templateId && !b.templateId) { + return 1; + } + + if (!a.templateId && b.templateId) { + return -1; + } + + if (a.name === "unsubscribe" || a.name === "subscribe") { + return 1; + } + + if (b.name === "unsubscribe" || b.name === "subscribe") { + return -1; + } + + if ( + a.name.includes("delivered") && + !b.name.includes("delivered") + ) { + return -1; + } + + return 0; + }) + .map((e) => { + return { + name: e.name, + value: e.id, + tag: e.templateId + ? e.name.includes("opened") + ? "On Open" + : "On Delivery" + : e.name === "unsubscribe" || e.name === "subscribe" + ? "Automated" + : undefined, + }; + })} + selectedValues={watch("events")} + /> + + {(errors.events as FieldError | undefined)?.message && ( + + {(errors.events as FieldError | undefined)?.message} + + )} + +
    + +
    + + setValue("notevents", e)} + values={events + .filter( + (e) => !e.campaignId && !watch("events").includes(e.id), + ) + .sort((a, b) => { + if (a.templateId && !b.templateId) { + return 1; + } + + if (!a.templateId && b.templateId) { + return -1; + } + + if (a.name === "unsubscribe" || a.name === "subscribe") { + return 1; + } + + if (b.name === "unsubscribe" || b.name === "subscribe") { + return -1; + } + + if ( + a.name.includes("delivered") && + !b.name.includes("delivered") + ) { + return -1; + } + + return 0; + }) + .map((e) => { + return { + name: e.name, + value: e.id, + tag: e.templateId + ? e.name.includes("opened") + ? "On Open" + : "On Delivery" + : e.name === "unsubscribe" || e.name === "subscribe" + ? "Automated" + : undefined, + }; + })} + selectedValues={watch("notevents")} + /> + + {(errors.notevents as FieldError | undefined)?.message && ( + + {(errors.notevents as FieldError | undefined)?.message} + + )} + +
    + +
    + +
    +
    + setValue("template", t)} + values={templates.map((t) => { + return { name: t.subject, value: t.id }; + })} + selectedValue={watch("template")} + /> + + {errors.template?.message && ( + + {errors.template.message} + + )} + +
    + + + + + + + Edit + + +
    +
    + +
    + +
    +
    + + setDelay({ + ...delay, + delay: Number.parseInt(e.target.value), + }) + } + /> +
    +
    + + setDelay({ + ...delay, + unit: t as "MINUTES" | "HOURS" | "DAYS", + }) + } + values={[ + { name: "Minutes", value: "MINUTES" }, + { name: "Hours", value: "HOURS" }, + { name: "Days", value: "DAYS" }, + ]} + selectedValue={delay.unit} + /> +
    +
    +
    + +
    + setValue("runOnce", !watch("runOnce"))} + /> +
    + +
    + { + e.preventDefault(); + return router.push("/actions"); + }} + className={ + "flex w-full justify-center rounded border border-neutral-300 bg-white px-6 py-2 text-base font-medium text-neutral-800 focus:outline-none focus:ring-2 focus:ring-neutral-800 focus:ring-offset-2 sm:mt-0 sm:w-auto sm:text-sm" + } + > + Cancel + + + + + Save + +
    +
    +
    + +
    + {related.length > 0 ? ( + related + .sort((a, b) => { + if (a.delay < b.delay) { + return -1; + } + if (a.delay > b.delay) { + return 1; + } + return 0; + }) + .map((r) => { + return ( + +
    +
    + + + +
    +
    +

    + {r.name} +

    +

    + Runs after{" "} + {r.events + .filter( + (e) => + action.events.filter( + (a: { id: string }) => a.id === e.id, + ).length > 0, + ) + .map((e) => e.name)}{" "} + and{" "} + { + r.events.filter((e) => { + return ( + action.events.filter( + (a: { id: string }) => a.id === e.id, + ).length === 0 + ); + }).length + }{" "} + other events +

    +
    + {r.delay === action.delay ? ( + Same delay + ) : r.delay > action.delay ? ( + + {`${dayjs.duration(r.delay - action.delay, "minutes").humanize()} after this action`} + + ) : ( + + {`${dayjs.duration(action.delay - r.delay, "minutes").humanize()} before this action`} + + )} +
    +
    +
    + + ); + }) + ) : ( +
    + +
    + )} +
    +
    +
    + + ); +} diff --git a/packages/dashboard/src/pages/actions/index.tsx b/packages/dashboard/src/pages/actions/index.tsx new file mode 100644 index 0000000..5bb3239 --- /dev/null +++ b/packages/dashboard/src/pages/actions/index.tsx @@ -0,0 +1,303 @@ +import dayjs from "dayjs"; +import { motion } from "framer-motion"; +import { Plus, Workflow } from "lucide-react"; +import Link from "next/link"; +import React from "react"; +import { + Alert, + Badge, + Card, + Empty, + FullscreenLoader, + Skeleton, +} from "../../components"; +import { Dashboard } from "../../layouts"; +import { useActions } from "../../lib/hooks/actions"; +import { useActiveProject } from "../../lib/hooks/projects"; + +/** + * + */ +export default function Index() { + const project = useActiveProject(); + const { data: actions } = useActions(); + + if (!project) { + return ; + } + + return ( + <> + + {actions?.length === 0 && ( + +
    +

    + Want us to help you get started? We can help you build your + first action in less than 5 minutes. +

    + + + Build an action + +
    +
    + )} + + + + + + New + + + + } + > + {actions ? ( + actions.length > 0 ? ( + <> +
    + {actions + .sort((a, b) => { + if (a.name < b.name) { + return -1; + } + + if (a.name > b.name) { + return 1; + } + + return 0; + }) + .map((a) => { + return ( + <> +
    +
    + + + +
    +
    +

    + {a.name} +

    +
    +
    +

    + Quick stats +

    +
    +
    + +

    + {a.triggers.length} +

    +
    + +
    + +

    + {a.triggers.length > 0 + ? "Last triggered" + : "Created"}{" "} + {dayjs() + .to( + a.triggers.length > 0 + ? a.triggers.sort((a, b) => { + return a.createdAt > + b.createdAt + ? -1 + : 1; + })[0].createdAt + : a.createdAt, + ) + .toString()} +

    +
    +
    + +

    + {a.emails.length > 0 + ? Math.round( + (a.emails.filter( + (e) => e.status === "OPENED", + ).length / + a.emails.length) * + 100, + ) + : 0} + % +

    +
    + {a.delay > 0 && ( +
    + +

    + {a.tasks.length} +

    +
    + )} +
    +
    +
    +

    + Properties +

    +
    +
    + +

    + + {a.runOnce + ? "Runs once per user" + : "Recurring"} + +

    +
    +
    + +

    + + {a.delay === 0 + ? "Instant" + : a.delay % 1440 === 0 + ? `${a.delay / 1440} day delay` + : a.delay % 60 === 0 + ? `${a.delay / 60} hour delay` + : `${a.delay} minute delay`} + +

    +
    +
    +
    +
    +
    +
    +
    +
    + + + + + + + Edit + +
    +
    +
    +
    + + ); + })} +
    + + ) : ( + <> + + + ) + ) : ( + + )} +
    +
    + + ); +} diff --git a/packages/dashboard/src/pages/actions/new.tsx b/packages/dashboard/src/pages/actions/new.tsx new file mode 100644 index 0000000..cd864e4 --- /dev/null +++ b/packages/dashboard/src/pages/actions/new.tsx @@ -0,0 +1,389 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { ActionSchemas } from "@plunk/shared"; +import type { Template } from "@prisma/client"; +import { AnimatePresence, motion } from "framer-motion"; +import { useRouter } from "next/router"; +import React, { useEffect, useState } from "react"; +import { type FieldError, useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { + Card, + Dropdown, + FullscreenLoader, + Input, + MultiselectDropdown, + Toggle, +} from "../../components"; +import { Dashboard } from "../../layouts"; +import { useActions } from "../../lib/hooks/actions"; +import { useEvents } from "../../lib/hooks/events"; +import { useActiveProject } from "../../lib/hooks/projects"; +import { useTemplates } from "../../lib/hooks/templates"; +import { network } from "../../lib/network"; + +interface ActionValues { + name: string; + runOnce: boolean; + delay: number; + template: string; + events: string[]; + notevents: string[]; +} + +/** + * + */ +export default function Index() { + const project = useActiveProject(); + const { mutate } = useActions(); + const { data: templates } = useTemplates(); + const { data: events } = useEvents(); + const router = useRouter(); + + const [delay, setDelay] = useState<{ + delay: number; + unit: "MINUTES" | "HOURS" | "DAYS"; + }>({ + delay: 0, + unit: "MINUTES", + }); + + useEffect(() => { + switch (delay.unit) { + case "MINUTES": + setValue("delay", delay.delay); + break; + case "HOURS": + setValue("delay", delay.delay * 60); + break; + case "DAYS": + setValue("delay", delay.delay * 24 * 60); + break; + } + }, [delay]); + + const { + register, + handleSubmit, + formState: { errors }, + setValue, + watch, + } = useForm({ + resolver: zodResolver(ActionSchemas.create), + defaultValues: { + template: "No template selected", + events: [], + notevents: [], + runOnce: false, + }, + }); + + if (!project || !templates || !events) { + return ; + } + + const create = async (data: ActionValues) => { + toast.promise( + network.mock( + project.secret, + "POST", + "/v1/actions", + { + ...data, + }, + ), + { + loading: "Creating new action", + success: () => { + void mutate(); + return "Created new action"; + }, + error: "Could not create new action!", + }, + ); + + await router.push("/actions"); + }; + + return ( + <> + + +
    + + +
    + + setValue("events", e)} + values={events + .filter( + (e) => !e.campaignId && !watch("notevents").includes(e.id), + ) + .sort((a, b) => { + if (a.templateId && !b.templateId) { + return 1; + } + + if (!a.templateId && b.templateId) { + return -1; + } + + if (a.name === "unsubscribe" || a.name === "subscribe") { + return 1; + } + + if (b.name === "unsubscribe" || b.name === "subscribe") { + return -1; + } + + if ( + a.name.includes("delivered") && + !b.name.includes("delivered") + ) { + return -1; + } + + return 0; + }) + .map((e) => { + return { + name: e.name, + value: e.id, + tag: e.templateId + ? e.name.includes("opened") + ? "On Open" + : "On Delivery" + : e.name === "unsubscribe" || e.name === "subscribe" + ? "Automated" + : undefined, + }; + })} + selectedValues={watch("events")} + /> + + {(errors.events as FieldError | undefined)?.message && ( + + {(errors.events as FieldError | undefined)?.message} + + )} + +
    + +
    + + setValue("notevents", e)} + values={events + .filter( + (e) => !e.campaignId && !watch("events").includes(e.id), + ) + .sort((a, b) => { + if (a.templateId && !b.templateId) { + return 1; + } + + if (!a.templateId && b.templateId) { + return -1; + } + + if (a.name === "unsubscribe" || a.name === "subscribe") { + return 1; + } + + if (b.name === "unsubscribe" || b.name === "subscribe") { + return -1; + } + + if ( + a.name.includes("delivered") && + !b.name.includes("delivered") + ) { + return -1; + } + + return 0; + }) + .map((e) => { + return { + name: e.name, + value: e.id, + tag: e.templateId + ? e.name.includes("opened") + ? "On Open" + : "On Delivery" + : e.name === "unsubscribe" || e.name === "subscribe" + ? "Automated" + : undefined, + }; + })} + selectedValues={watch("notevents")} + /> + + {(errors.notevents as FieldError | undefined)?.message && ( + + {(errors.notevents as FieldError | undefined)?.message} + + )} + +
    + +
    + + setValue("template", t)} + values={templates.map((t) => { + return { name: t.subject, value: t.id }; + })} + selectedValue={watch("template")} + /> + + {errors.template?.message && ( + + {errors.template.message} + + )} + +
    + +
    + +
    +
    + + setDelay({ + ...delay, + delay: Number.parseInt(e.target.value), + }) + } + /> +
    +
    + + setDelay({ + ...delay, + unit: t as "MINUTES" | "HOURS" | "DAYS", + }) + } + values={[ + { name: "Minutes", value: "MINUTES" }, + { name: "Hours", value: "HOURS" }, + { name: "Days", value: "DAYS" }, + ]} + selectedValue={delay.unit} + /> +
    +
    +
    + +
    + setValue("runOnce", !watch("runOnce"))} + /> +
    + +
    + { + e.preventDefault(); + return router.push("/actions"); + }} + className={ + "flex w-full justify-center rounded border border-neutral-300 bg-white px-6 py-2 text-base font-medium text-neutral-700 focus:outline-none focus:ring-2 focus:ring-neutral-800 focus:ring-offset-2 sm:mt-0 sm:w-auto sm:text-sm" + } + > + Cancel + + + + + + + + Create + +
    +
    +
    +
    + + ); +} diff --git a/packages/dashboard/src/pages/analytics/clicks.tsx b/packages/dashboard/src/pages/analytics/clicks.tsx new file mode 100644 index 0000000..55f5ca4 --- /dev/null +++ b/packages/dashboard/src/pages/analytics/clicks.tsx @@ -0,0 +1,95 @@ +import {useActiveProject} from '../../lib/hooks/projects'; +import {useAnalytics} from '../../lib/hooks/analytics'; +import {AnalyticsTabs, Card, FullscreenLoader} from '../../components'; +import React from 'react'; +import {Dashboard} from '../../layouts'; +import {Ring} from '@uiball/loaders'; +import {Bar, BarChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis} from 'recharts'; +import {valueFormatter} from './index'; + +/** + * + */ +export default function Index() { + const project = useActiveProject(); + const {data: analytics} = useAnalytics(); + + if (!project) { + return ; + } + + return ( + <> + + + +
    + + {analytics ? ( + <> + + + + + } tickSize={0} width={5} /> + + { + return ( + + + {payload.value.length > 5 ? `${payload.value.substring(0, 20)}...` : payload.value} + + + ); + }} + /> + + { + if (active && payload?.length) { + const dataPoint = payload[0]; + return ( +
    +

    {`${label}`}

    +

    {valueFormatter(dataPoint.value as number)}

    +
    + ); + } + + return null; + }} + /> + + +
    +
    + + ) : ( + <> +
    + +
    + + )} +
    +
    +
    + + ); +} diff --git a/packages/dashboard/src/pages/analytics/index.tsx b/packages/dashboard/src/pages/analytics/index.tsx new file mode 100644 index 0000000..8bb304a --- /dev/null +++ b/packages/dashboard/src/pages/analytics/index.tsx @@ -0,0 +1,555 @@ +import { AnalyticsTabs, Card, FullscreenLoader } from "../../components"; +import { Dashboard } from "../../layouts"; +import { useActiveProject } from "../../lib/hooks/projects"; + +import { Ring } from "@uiball/loaders"; +import dayjs from "dayjs"; +import { ArrowDown, ArrowUp } from "lucide-react"; +import React from "react"; +import { + Area, + AreaChart, + CartesianGrid, + Cell, + Pie, + PieChart, + ResponsiveContainer, + Tooltip, + XAxis, + YAxis, +} from "recharts"; +import { useAnalytics } from "../../lib/hooks/analytics"; + +export const valueFormatter = (number: number) => { + if (number > 999999999) { + return `${Intl.NumberFormat("us") + .format(number / 1000000000) + .toString()}B`; + } + + if (number > 999999) { + return `${Intl.NumberFormat("us") + .format(number / 1000000) + .toString()}M`; + } + + if (number > 999) { + return `${Intl.NumberFormat("us") + .format(number / 1000) + .toString()}K`; + } + + return Intl.NumberFormat("us").format(number).toString(); +}; + +/** + * + */ +export default function Index() { + const project = useActiveProject(); + const { data: analytics } = useAnalytics(); + + if (!project) { + return ; + } + + return ( + <> + + + +
    + + {analytics ? ( +
    +
    +

    Bounce Rate

    +

    + <> + {( + (analytics.emails.bounced / analytics.emails.total) * + 100 + ).toFixed(2)} + % + +

    +
    +
    + {analytics.emails.bounced / analytics.emails.total > + analytics.emails.bouncedPrev / analytics.emails.totalPrev ? ( + <> + + {Number.isNaN( + (analytics.emails.bounced / analytics.emails.total - + analytics.emails.bouncedPrev / + analytics.emails.totalPrev) * + 100, + ) + ? 0 + : ( + (analytics.emails.bounced / + analytics.emails.total - + analytics.emails.bouncedPrev / + analytics.emails.totalPrev) * + 100 + ).toFixed(2)} + % + + + + ) : ( + <> + + {Number.isNaN( + (analytics.emails.bounced / analytics.emails.total - + analytics.emails.bouncedPrev / + analytics.emails.totalPrev) * + 100, + ) + ? 0 + : ( + (analytics.emails.bounced / + analytics.emails.total - + analytics.emails.bouncedPrev / + analytics.emails.totalPrev) * + 100 + ).toFixed(2)} + % + + + + )} +
    +
    + ) : ( + <> +
    + +
    + + )} +
    + + {analytics ? ( +
    +
    +

    Spam Rate

    +

    + <> + {( + (analytics.emails.complaint / analytics.emails.total) * + 100 + ).toFixed(2)} + % + +

    +
    +
    + {analytics.emails.complaint / analytics.emails.total > + analytics.emails.complaintPrev / + analytics.emails.totalPrev ? ( + <> + + {Number.isNaN( + (analytics.emails.complaint / analytics.emails.total - + analytics.emails.complaintPrev / + analytics.emails.totalPrev) * + 100, + ) + ? 0 + : ( + (analytics.emails.complaint / + analytics.emails.total - + analytics.emails.complaintPrev / + analytics.emails.totalPrev) * + 100 + ).toFixed(2)} + % + + + + ) : ( + <> + + {Number.isNaN( + (analytics.emails.complaint / analytics.emails.total - + analytics.emails.complaintPrev / + analytics.emails.totalPrev) * + 100, + ) + ? 0 + : ( + (analytics.emails.complaint / + analytics.emails.total - + analytics.emails.complaintPrev / + analytics.emails.totalPrev) * + 100 + ).toFixed(2)} + % + + + + )} +
    +
    + ) : ( + <> +
    + +
    + + )} +
    + + {analytics ? ( + <> + + { + return ( + new Date(a.day).getTime() - new Date(b.day).getTime() + ); + }) + .map((i) => { + return { + day: dayjs(i.day).format("MMM DD"), + count: i.count, + }; + })} + margin={{ + top: 20, + right: 20, + left: 20, + bottom: 0, + }} + > + + + + + + + + + + + { + return ( + + + {payload.value} + + + ); + }} + /> + + { + if (active && payload?.length) { + // Customize the tooltip content here + const dataPoint = payload[0]; + return ( +
    +

    {`${label}`}

    +

    + {valueFormatter(dataPoint.value as number)} +

    +
    + ); + } + + return null; + }} + /> + + +
    +
    + + ) : ( + <> +
    + +
    + + )} +
    + + {analytics ? ( + <> + + + { + if (active && payload?.length) { + // Customize the tooltip content here + const dataPoint = payload[0]; + return ( +
    +

    {`${dataPoint.name}`}

    +

    + {valueFormatter(dataPoint.value as number)} +

    +
    + ); + } + + return null; + }} + /> + + { + const radius = + innerRadius + (outerRadius - innerRadius) * 0.5; + const x = + cx + radius * Math.cos((-midAngle * Math.PI) / 180); + const y = + cy + radius * Math.sin((-midAngle * Math.PI) / 180); + + if (percent < 0.1) { + return null; + } + + return ( + 0.5 ? "white" : "#666"} + className={"text-sm font-semibold"} + textAnchor={x > cx ? "start" : "middle"} + dominantBaseline="central" + > + {`${(percent * 100).toFixed(0)}%`} + + ); + }} + outerRadius={90} + fill="#8884d8" + dataKey="value" + > + {[ + { + name: "Subscribed", + value: analytics.contacts.subscribed, + }, + { + name: "Unsubscribed", + value: analytics.contacts.unsubscribed, + }, + ].map((entry, index) => ( + + ))} + +
    +
    + + ) : ( + <> +
    + +
    + + )} +
    + + {analytics ? ( + <> + + + { + if (active && payload?.length) { + // Customize the tooltip content here + const dataPoint = payload[0]; + return ( +
    +

    {`${dataPoint.name}`}

    +

    + {valueFormatter(dataPoint.value as number)} +

    +
    + ); + } + + return null; + }} + /> + + { + const radius = + innerRadius + (outerRadius - innerRadius) * 0.5; + const x = + cx + radius * Math.cos((-midAngle * Math.PI) / 180); + const y = + cy + radius * Math.sin((-midAngle * Math.PI) / 180); + + if (percent < 0.1) { + return null; + } + + return ( + 0.5 ? "white" : "#666"} + className={"text-sm font-semibold"} + textAnchor={x > cx ? "start" : "middle"} + dominantBaseline="central" + > + {`${(percent * 100).toFixed(0)}%`} + + ); + }} + outerRadius={90} + fill="#8884d8" + dataKey="value" + > + {[ + { name: "Opened", value: analytics.emails.opened }, + + { + name: "Unopened", + value: + analytics.emails.total - + analytics.emails.opened - + analytics.emails.bounced - + analytics.emails.complaint, + }, + ].map((entry, index) => ( + + ))} + +
    +
    + + ) : ( + <> +
    + +
    + + )} +
    +
    +
    + + ); +} diff --git a/packages/dashboard/src/pages/auth/login.tsx b/packages/dashboard/src/pages/auth/login.tsx new file mode 100644 index 0000000..f2a4016 --- /dev/null +++ b/packages/dashboard/src/pages/auth/login.tsx @@ -0,0 +1,263 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { UserSchemas } from "@plunk/shared"; +import type { User } from "@prisma/client"; +import { AnimatePresence, motion } from "framer-motion"; +import Image from "next/image"; +import Link from "next/link"; +import { useRouter } from "next/router"; +import React, { useState } from "react"; +import { useForm } from "react-hook-form"; +import logo from "../../../public/assets/logo.png"; +import { FullscreenLoader, Redirect } from "../../components"; +import { useUser } from "../../lib/hooks/users"; +import { network } from "../../lib/network"; + +/** + * + */ +interface AuthValues { + password: string; + email: string; + auth: string; +} + +/** + * + */ +export default function Index() { + const router = useRouter(); + + const { data: user, error, mutate } = useUser(); + + const [submitted, setSubmitted] = useState(false); + const [hidePassword, setHidePassword] = useState(true); + + const { + register, + handleSubmit, + formState: { errors }, + setError, + } = useForm({ + resolver: zodResolver(UserSchemas.credentials), + }); + + if (user && !error) { + return ; + } + + if (!user && !error) { + return ; + } + + const login = async (data: AuthValues) => { + setSubmitted(true); + const result = await network.fetch< + | { + success: true; + data: User; + } + | { + success: false; + data: string; + } + | { + success: "redirect"; + redirect: string; + }, + typeof UserSchemas.credentials + >("POST", "/auth/login", { + ...data, + }); + + if (result.success === "redirect") { + return router.push(result.redirect); + } + + if (result.success) { + await mutate(result.data); + + return router.push("/"); + } + setError("auth", { message: result.data }); + + setSubmitted(false); + }; + + return ( + <> +
    +
    + {"Plunk +

    + Sign in to your account +

    +
    + +
    +
    +
    +
    + +
    + +
    + + {errors.email?.message && ( + + {errors.email.message} + + )} + +
    + +
    + +
    + +
    + setHidePassword(!hidePassword)} + className="h-5 w-5 text-neutral-400" + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 20 20" + fill="currentColor" + aria-hidden="true" + > + {hidePassword ? ( + <> + + + + ) : ( + <> + + + + )} + +
    +
    + + {errors.password?.message && ( + + Password must be at least 6 characters long + + )} + +
    + +
    + + {submitted ? ( + + + + + ) : ( + "Sign in" + )} + + + {errors.auth?.message && ( + + {errors.auth.message} + + )} + +
    +
    +
    +
    + + Want to create an account instead? + +
    +
    +
    + + ); +} diff --git a/packages/dashboard/src/pages/auth/logout.tsx b/packages/dashboard/src/pages/auth/logout.tsx new file mode 100644 index 0000000..d9ed9da --- /dev/null +++ b/packages/dashboard/src/pages/auth/logout.tsx @@ -0,0 +1,29 @@ +import { useRouter } from "next/router"; +import { useEffect } from "react"; +import { FullscreenLoader } from "../../components/"; +import { useUser } from "../../lib/hooks/users"; +import { network } from "../../lib/network"; + +/** + * + */ +export default function Index() { + const router = useRouter(); + + const { error, mutate } = useUser(); + + if (error) { + void router.push("/"); + } + + useEffect(() => { + void network.fetch("GET", "/auth/logout").then(async (success) => { + if (success) { + await mutate(null); + await router.push("/"); + } + }); + }, [mutate, router.push]); + + return ; +} diff --git a/packages/dashboard/src/pages/auth/reset.tsx b/packages/dashboard/src/pages/auth/reset.tsx new file mode 100644 index 0000000..f8f2563 --- /dev/null +++ b/packages/dashboard/src/pages/auth/reset.tsx @@ -0,0 +1,188 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { UserSchemas, UtilitySchemas } from "@plunk/shared"; +import { AnimatePresence, motion } from "framer-motion"; +import { useRouter } from "next/router"; +import React, { useState } from "react"; +import { useForm } from "react-hook-form"; +import { Redirect } from "../../components"; +import { network } from "../../lib/network"; + +interface ResetValues { + password: string; +} + +/** + * + */ +export default function Index() { + const router = useRouter(); + + if (!router.query.id) { + return ; + } + + const [submitted, setSubmitted] = useState(false); + const [hidePassword, setHidePassword] = useState(true); + + const { + register, + handleSubmit, + formState: { errors }, + } = useForm({ + resolver: zodResolver(UserSchemas.credentials.pick({ password: true })), + }); + + const resetPassword = async (data: ResetValues) => { + const schema = UtilitySchemas.id.merge( + UserSchemas.credentials.pick({ password: true }), + ); + + setSubmitted(true); + await network.fetch< + { + success: true; + }, + typeof schema + >("POST", "/auth/reset", { + id: router.query.id as string, + ...data, + }); + + return router.push("/auth/login"); + }; + + return ( +
    +
    +
    + + + + +
    +
    +

    + Reset password +

    +

    + Please enter your new password and confirm it. +

    +
    +
    +
    + +
    + +
    + setHidePassword(!hidePassword)} + className="h-5 w-5 text-neutral-400" + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 20 20" + fill="currentColor" + aria-hidden="true" + > + {hidePassword ? ( + <> + + + + ) : ( + <> + + + + )} + +
    +
    + + {errors.password?.message && ( + + Password must be atleast 6 characters long + + )} + +
    + +
    + + {submitted ? ( + + + + + ) : ( + "Change password" + )} + +
    +
    +
    +
    + ); +} diff --git a/packages/dashboard/src/pages/auth/signup.tsx b/packages/dashboard/src/pages/auth/signup.tsx new file mode 100644 index 0000000..610dad1 --- /dev/null +++ b/packages/dashboard/src/pages/auth/signup.tsx @@ -0,0 +1,266 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { UserSchemas } from "@plunk/shared"; +import type { User } from "@prisma/client"; +import { AnimatePresence, motion } from "framer-motion"; +import Image from "next/image"; +import Link from "next/link"; +import { useRouter } from "next/router"; +import React, { useState } from "react"; +import { useForm } from "react-hook-form"; +import logo from "../../../public/assets/logo.png"; +import { FullscreenLoader, Redirect } from "../../components"; +import { useUser } from "../../lib/hooks/users"; +import { network } from "../../lib/network"; + +interface AuthValues { + password: string; + email: string; + terms: boolean; + auth: string; +} + +/** + * + */ +export default function Index() { + const router = useRouter(); + + if (!router.isReady) { + return ; + } + + const { data: user, error, mutate } = useUser(); + + const [submitted, setSubmitted] = useState(false); + const [hidePassword, setHidePassword] = useState(true); + + const { + register, + handleSubmit, + formState: { errors }, + setError, + } = useForm({ + defaultValues: { email: (router.query.email as string | undefined) ?? "" }, + resolver: zodResolver(UserSchemas.credentials), + }); + + if (user && !error) { + return ; + } + + if (!user && !error) { + return ; + } + + const signup = async (data: AuthValues) => { + setSubmitted(true); + + const result = await network.fetch< + | { + success: true; + data: User; + } + | { + success: false; + data: string; + }, + typeof UserSchemas.credentials + >("POST", "/auth/signup", { + ...data, + }); + + if (result.success) { + await mutate(result.data); + + return router.push("/new"); + } + + setError("auth", { message: result.data }); + + setSubmitted(false); + }; + + return ( + <> +
    +
    +
    +
    + {"Plunk +

    + Create a Plunk account +

    +
    + + Already have an account? + +
    +
    + +
    +
    +
    +
    + +
    + +
    + + {errors.email?.message && ( + + {errors.email.message} + + )} + +
    + +
    + +
    + +
    + setHidePassword(!hidePassword)} + className="h-5 w-5 text-neutral-400" + xmlns="http://www.w3.org/2000/svg" + viewBox="0 0 20 20" + fill="currentColor" + aria-hidden="true" + > + {hidePassword ? ( + <> + + + + ) : ( + <> + + + + )} + +
    +
    + + {errors.password?.message && ( + + Password must be atleast 6 characters long + + )} + +
    + +
    + + {submitted ? ( + + + + + ) : ( + "Create account" + )} + + + {errors.auth?.message && ( + + {errors.auth.message} + + )} + +
    +
    +
    +
    +
    +
    +
    +
    + + ); +} diff --git a/packages/dashboard/src/pages/campaigns/[id].tsx b/packages/dashboard/src/pages/campaigns/[id].tsx new file mode 100644 index 0000000..32c591a --- /dev/null +++ b/packages/dashboard/src/pages/campaigns/[id].tsx @@ -0,0 +1,1098 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { CampaignSchemas, type UtilitySchemas } from "@plunk/shared"; +import type { Campaign, Template } from "@prisma/client"; +import { Ring } from "@uiball/loaders"; +import dayjs from "dayjs"; +import { AnimatePresence, motion } from "framer-motion"; +import { Eye, Save, Search, Users2, XIcon } from "lucide-react"; +import Link from "next/link"; +import { useRouter } from "next/router"; +import React, { useEffect, useState } from "react"; +import { type FieldError, useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { + Alert, + Badge, + Card, + Dropdown, + Editor, + FullscreenLoader, + Input, + Modal, + MultiselectDropdown, + Table, +} from "../../components"; +import { Dashboard } from "../../layouts"; +import { useCampaign, useCampaigns } from "../../lib/hooks/campaigns"; +import { useContacts } from "../../lib/hooks/contacts"; +import { useEventsWithoutTriggers } from "../../lib/hooks/events"; +import { useActiveProject } from "../../lib/hooks/projects"; +import { network } from "../../lib/network"; + +interface CampaignValues { + subject: string; + body: string; + recipients: string[]; + style: "PLUNK" | "HTML"; +} + +/** + * + */ +export default function Index() { + const router = useRouter(); + + if (!router.isReady) { + return ; + } + + const project = useActiveProject(); + const { mutate: campaignsMutate } = useCampaigns(); + const { data: campaign, mutate: campaignMutate } = useCampaign( + router.query.id as string, + ); + const { data: contacts } = useContacts(0); + const { data: events } = useEventsWithoutTriggers(); + + const [query, setQuery] = useState<{ + events?: string[]; + last?: "day" | "week" | "month"; + data?: string; + value?: string; + notevents?: string[]; + notlast?: "day" | "week" | "month"; + }>({}); + const [confirmModal, setConfirmModal] = useState(false); + const [paymentModal, setPaymentModal] = useState(false); + const [advancedSelector, setSelector] = useState(false); + const [delay, setDelay] = useState(0); + + const { + register, + handleSubmit, + formState: { errors }, + watch, + reset, + setValue, + } = useForm({ + resolver: zodResolver(CampaignSchemas.update), + defaultValues: { recipients: [], body: undefined }, + }); + + useEffect(() => { + if (!campaign) { + return; + } + + reset({ + ...campaign, + recipients: campaign.recipients.map((r: { id: string }) => r.id), + }); + }, [reset, campaign]); + + if ( + !project || + !campaign || + !events || + (watch("body") as string | undefined) === undefined + ) { + return ; + } + + const selectQuery = () => { + if (!contacts) { + return; + } + + let filteredContacts = contacts.contacts; + + if (query.events && query.events.length > 0) { + query.events.map((e) => { + filteredContacts = filteredContacts.filter((c) => + c.triggers.some((t) => t.eventId === e), + ); + }); + } + + if (query.last) { + filteredContacts = filteredContacts.filter((c) => { + if (c.triggers.length === 0) { + return false; + } + + const lastTrigger = c.triggers.sort((a, b) => + a.createdAt > b.createdAt ? -1 : 1, + ); + + if (lastTrigger.length === 0) { + return false; + } + + return dayjs(lastTrigger[0].createdAt).isAfter( + dayjs().subtract(1, query.last), + ); + }); + } + + if (query.notevents && query.notevents.length > 0 && query.notlast) { + query.notevents.map((e) => { + filteredContacts = filteredContacts.filter((c) => { + if (c.triggers.length === 0) { + return true; + } + + const lastTrigger = c.triggers + .filter((t) => t.eventId === e) + .sort((a, b) => (a.createdAt > b.createdAt ? -1 : 1)); + + if (lastTrigger.length === 0) { + return true; + } + + return dayjs(lastTrigger[0].createdAt).isAfter( + dayjs().subtract(1, query.last), + ); + }); + }); + } else if (query.notevents && query.notevents.length > 0) { + query.notevents.map((e) => { + filteredContacts = filteredContacts.filter((c) => + c.triggers.every((t) => t.eventId !== e), + ); + }); + } else if (query.notlast) { + filteredContacts = filteredContacts.filter((c) => { + if (c.triggers.length === 0) { + return true; + } + + const lastTrigger = c.triggers.sort((a, b) => + a.createdAt > b.createdAt ? -1 : 1, + ); + + if (lastTrigger.length === 0) { + return true; + } + + return !dayjs(lastTrigger[0].createdAt).isAfter( + dayjs().subtract(1, query.notlast), + ); + }); + } + + if (query.data) { + filteredContacts = filteredContacts.filter((c) => { + if (!c.data) { + return false; + } + + return JSON.parse(c.data)[query.data as string]; + }); + } + + if (query.data && query.value) { + filteredContacts = filteredContacts.filter((c) => { + if (!c.data) { + return false; + } + + return Array.isArray(JSON.parse(c.data)[query.data as string]) + ? JSON.parse(c.data)[query.data as string].includes(query.value) + : JSON.parse(c.data)[query.data as string] === query.value; + }); + } + + setValue( + "recipients", + filteredContacts.map((c) => c.id), + ); + }; + + const send = async (data: CampaignValues) => { + setConfirmModal(false); + + toast.success( + "Saved your campaign. Starting delivery now, please hold on!", + ); + + await network.mock( + project.secret, + "PUT", + "/v1/campaigns", + + data.recipients.length === + contacts?.contacts.filter((c) => c.subscribed).length + ? { id: campaign.id, ...data, recipients: ["all"] } + : { + id: campaign.id, + ...data, + }, + ); + + toast.promise( + network.mock( + project.secret, + "POST", + "/v1/campaigns/send", + { + id: campaign.id, + live: true, + delay, + }, + ), + + { + loading: "Starting delivery...", + success: () => { + void campaignMutate(); + void campaignsMutate(); + + return `Started delivery of your campaign to ${watch("recipients").length} recipients`; + }, + error: () => { + return "Could not send your campaign!"; + }, + }, + ); + }; + + const sendTest = async (data: CampaignValues) => { + await network.mock( + project.secret, + "PUT", + "/v1/campaigns", + { + id: campaign.id, + ...data, + }, + ); + + toast.promise( + network.mock( + project.secret, + "POST", + "/v1/campaigns/send", + { + id: campaign.id, + live: false, + delay: 0, + }, + ), + + { + loading: "Sending you a test campaign", + success: "Sent all project members a test campaign", + error: "Could not send your campaign!", + }, + ); + }; + + const update = (data: CampaignValues) => { + toast.promise( + network.mock( + project.secret, + "PUT", + "/v1/campaigns", + { + id: campaign.id, + ...data, + }, + ), + { + loading: "Saving your campaign", + success: () => { + void campaignMutate(); + void campaignsMutate(); + return "Saved your campaign"; + }, + error: "Could not save your campaign!", + }, + ); + }; + + const duplicate = async (e: { preventDefault: () => void }) => { + e.preventDefault(); + toast.promise( + network.mock( + project.secret, + "POST", + "/v1/campaigns/duplicate", + { + id: campaign.id, + }, + ), + { + loading: "Duplicating your campaign", + success: () => { + void campaignMutate(); + void campaignsMutate(); + return "Duplicated your campaign"; + }, + error: "Could not duplicate your campaign!", + }, + ); + + await router.push("/campaigns"); + }; + + const remove = async (e: { preventDefault: () => void }) => { + e.preventDefault(); + toast.promise( + network.mock( + project.secret, + "DELETE", + "/v1/campaigns", + { + id: campaign.id, + }, + ), + { + loading: "Deleting your campaign", + success: () => { + void campaignMutate(); + void campaignsMutate(); + return "Deleted your campaign"; + }, + error: "Could not delete your campaign!", + }, + ); + + await router.push("/campaigns"); + }; + + return ( + <> + setConfirmModal(!confirmModal)} + onAction={handleSubmit(send)} + type={"info"} + title={"Send campaign"} + description={`Once you start sending this campaign to ${watch("recipients").length} contacts, you can no longer make changes or undo it.`} + > + + setDelay(Number.parseInt(val))} + values={[ + { + name: "Send immediately", + value: "0", + }, + { + name: "In an hour", + value: "60", + }, + { + name: "In 6 hours", + value: "360", + }, + { + name: "In 12 hours", + value: "720", + }, + { + name: "In 24 hours", + value: "1440", + }, + ]} + selectedValue={delay.toString()} + /> + + + + + + + } + > +
    + + + {contacts ? ( + <> +
    + + setValue("recipients", c)} + values={contacts.contacts + .filter((c) => c.subscribed) + .map((c) => { + return { name: c.email, value: c.id }; + })} + selectedValues={watch("recipients")} + /> + + {(errors.recipients as FieldError | undefined)?.message && ( + + {(errors.recipients as FieldError | undefined)?.message} + + )} + +
    + +
    + {campaign.status === "DRAFT" && ( + <> + + + + )} +
    + + + {advancedSelector && ( + +
    + + + setQuery( + e.length > 0 + ? { ...query, events: e } + : { + ...query, + events: undefined, + last: undefined, + }, + ) + } + values={[ + ...events + .filter((e) => !query.notevents?.includes(e.id)) + .sort((a, b) => { + if (!a.templateId && !a.campaignId) { + return -1; + } + if (!b.templateId && !b.campaignId) { + return 1; + } + + if ( + a.name.includes("delivered") && + !b.name.includes("delivered") + ) { + return -1; + } + + return 0; + }) + .map((e) => { + return { + name: e.name, + value: e.id, + tag: + e.templateId ?? e.campaignId + ? e.name.includes("opened") + ? "On Open" + : "On Delivery" + : undefined, + }; + }), + ]} + selectedValues={query.events ?? []} + /> +
    + +
    + {query.events && query.events.length > 0 && ( + <> + + + setQuery({ + ...query, + last: + (e as "" | "day" | "week" | "month") === "" + ? undefined + : (e as "day" | "week" | "month"), + }) + } + values={[ + { name: "Anytime", value: "" }, + { name: "In the last day", value: "day" }, + { name: "In the last week", value: "week" }, + { name: "In the last month", value: "month" }, + ]} + selectedValue={query.last ?? ""} + /> + + )} +
    + +
    + + { + setQuery( + e.length > 0 + ? { ...query, notevents: e } + : { + ...query, + notevents: undefined, + notlast: undefined, + }, + ); + }} + values={[ + ...events + .filter((e) => !query.events?.includes(e.id)) + .sort((a, b) => { + if (!a.templateId && !a.campaignId) { + return -1; + } + if (!b.templateId && !b.campaignId) { + return 1; + } + + if ( + a.name.includes("delivered") && + !b.name.includes("delivered") + ) { + return -1; + } + + return 0; + }) + .map((e) => { + return { + name: e.name, + value: e.id, + tag: + e.templateId ?? e.campaignId + ? e.name.includes("opened") + ? "On Open" + : "On Delivery" + : undefined, + }; + }), + ]} + selectedValues={query.notevents ?? []} + /> +
    + +
    + {query.notevents && query.notevents.length > 0 && ( + <> + + + setQuery({ + ...query, + notlast: + (e as "" | "day" | "week" | "month") === "" + ? undefined + : (e as "day" | "week" | "month"), + }) + } + values={[ + { name: "Anytime", value: "" }, + { name: "In the last day", value: "day" }, + { name: "In the last week", value: "week" }, + { name: "In the last month", value: "month" }, + ]} + selectedValue={query.notlast ?? ""} + /> + + )} +
    + +
    + + + setQuery({ + ...query, + data: e === "" ? undefined : e, + }) + } + values={[ + { name: "Any parameter", value: "" }, + ...new Set( + contacts.contacts + .filter((c) => c.data) + .map((c) => { + return Object.keys( + JSON.parse(c.data ?? "{}"), + ); + }) + .reduce((acc, val) => acc.concat(val), []), + ), + ].map((k) => + typeof k === "string" ? { name: k, value: k } : k, + )} + selectedValue={query.data ?? ""} + /> +
    + +
    + {query.data && ( + <> + + + + setQuery({ + ...query, + value: e === "" ? undefined : e, + }) + } + values={[ + { name: "Any value", value: "" }, + ...new Set( + contacts.contacts + .filter( + (c) => + c.data && + JSON.parse(c.data)[query.data ?? ""], + ) + .map((c) => { + return JSON.parse(c.data ?? "{}")[ + query.data ?? "" + ]; + }) + .reduce((acc, val) => acc.concat(val), []), + ), + ].map((k) => + typeof k === "string" + ? { + name: k, + value: k, + } + : (k as { + name: string; + value: string; + }), + )} + selectedValue={query.value ?? ""} + /> + + )} +
    + +
    + { + e.preventDefault(); + selectQuery(); + }} + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.9 }} + className={ + "ml-auto flex items-center justify-center gap-x-0.5 rounded bg-neutral-800 px-8 py-2 text-center text-sm font-medium text-white" + } + > + + + + + Select contacts + +
    +
    + )} +
    + + ) : ( + campaign.status === "DRAFT" && ( + <> +
    + +
    +

    + Hang on! +

    +

    + We're still loading your contacts. This might take up to + a minute. You can already start writing your campaign in + the editor below. +

    +
    +
    + + ) + )} + + + {watch("recipients").length >= 10 && + campaign.status !== "DELIVERED" && ( + + + Your campaign will be sent out in batches of 80 recipients + each. It will be delivered to all contacts{" "} + {dayjs().to( + dayjs().add( + Math.ceil(watch("recipients").length / 80), + "minutes", + ), + )} + + + )} + + + {campaign.status !== "DRAFT" && + (campaign.emails.length === 0 ? ( +
    + +
    +

    + Hang on! +

    +

    + We are still sending your campaign. Emails will start + appearing here once they are sent. +

    +
    +
    + ) : ( +
    + { + return { + Email: e.contact.email, + Status: ( + + {e.status.at(0)?.toUpperCase() + + e.status.slice(1).toLowerCase()} + + ), + View: ( + + + + ), + }; + }, + )} + /> + + ))} + +
    + { + setValue("body", value); + setValue("style", type); + }} + modeSwitcher={campaign.status === "DRAFT"} + /> + + {errors.body?.message && ( + + {errors.body.message} + + )} + +
    + +
    + {campaign.status === "DRAFT" ? ( + <> + + + + + + Send test to {project.name}'s members + + { + e.preventDefault(); + setConfirmModal(true); + }} + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.9 }} + className={ + "ml-auto mt-6 flex items-center gap-x-0.5 rounded bg-neutral-800 px-6 py-2 text-center text-sm font-medium text-white" + } + > + + + + + Save & Send + + + + Save + + + ) : null} +
    + + + + + ); +} diff --git a/packages/dashboard/src/pages/campaigns/index.tsx b/packages/dashboard/src/pages/campaigns/index.tsx new file mode 100644 index 0000000..b68bfed --- /dev/null +++ b/packages/dashboard/src/pages/campaigns/index.tsx @@ -0,0 +1,257 @@ +import { motion } from "framer-motion"; +import { Plus, Send } from "lucide-react"; +import Link from "next/link"; +import React from "react"; +import { Badge, Card, Empty, Skeleton } from "../../components"; +import { Dashboard } from "../../layouts"; +import { useCampaigns } from "../../lib/hooks/campaigns"; + +/** + * + */ +export default function Index() { + const { data: campaigns } = useCampaigns(); + + return ( + <> + + + + + + New + + + + } + > + {campaigns ? ( + campaigns.length > 0 ? ( + <> +
    + {campaigns.map((c) => { + return ( + <> +
    +
    + + + +
    +
    +

    + {c.subject} +

    +
    +
    +

    + Quick Stats +

    +
    + {c.status === "DELIVERED" ? ( + <> +
    + +

    + {c.emails.length > 0 + ? Math.round( + (c.emails.filter( + (e) => e.status === "OPENED", + ).length / + c.emails.length) * + 100, + ) + : 0} + % +

    +
    + + {c.tasks.length > 0 && ( +
    + +

    + {c.tasks.length} +

    +
    + )} + + ) : ( + <> +
    + +

    + Awaiting delivery +

    +
    + + )} +
    +
    +
    +

    + Properties +

    +
    +
    + +

    + {c.recipients.length} +

    +
    + +
    + +

    + {c.status === "DRAFT" ? ( + Draft + ) : ( + 0 + ? "info" + : "success" + } + > + {c.tasks.length > 0 + ? "Sending" + : "Delivered"} + + )} +

    +
    +
    +
    +
    +
    +
    +
    +
    + + + {c.status === "DELIVERED" ? ( + <> + + + + ) : ( + <> + + + + )} + + + + {c.status === "DELIVERED" ? "View" : "Edit"} + + +
    +
    +
    +
    + + ); + })} +
    + + ) : ( + + ) + ) : ( + + )} +
    +
    + + ); +} diff --git a/packages/dashboard/src/pages/campaigns/new.tsx b/packages/dashboard/src/pages/campaigns/new.tsx new file mode 100644 index 0000000..06a2f7d --- /dev/null +++ b/packages/dashboard/src/pages/campaigns/new.tsx @@ -0,0 +1,738 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { CampaignSchemas } from "@plunk/shared"; +import type { Campaign } from "@prisma/client"; +import { Ring } from "@uiball/loaders"; +import dayjs from "dayjs"; +import { AnimatePresence, motion } from "framer-motion"; +import { Search, Users2, XIcon } from "lucide-react"; +import { useRouter } from "next/router"; +import React, { useState } from "react"; +import { type FieldError, useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { + Alert, + Card, + Dropdown, + Editor, + FullscreenLoader, + Input, + MultiselectDropdown, +} from "../../components"; +import { Dashboard } from "../../layouts"; +import { useCampaigns } from "../../lib/hooks/campaigns"; +import { useContacts } from "../../lib/hooks/contacts"; +import { useEventsWithoutTriggers } from "../../lib/hooks/events"; +import { useActiveProject } from "../../lib/hooks/projects"; +import { network } from "../../lib/network"; + +interface CampaignValues { + subject: string; + body: string; + recipients: string[]; + style: "PLUNK" | "HTML"; +} + +const templates = { + blank: { + subject: "", + body: "", + }, +}; + +/** + * + */ +export default function Index() { + const router = useRouter(); + + const project = useActiveProject(); + const { mutate } = useCampaigns(); + const { data: contacts } = useContacts(0); + const { data: events } = useEventsWithoutTriggers(); + + const [query, setQuery] = useState<{ + events?: string[]; + last?: "day" | "week" | "month"; + data?: string; + value?: string; + notevents?: string[]; + notlast?: "day" | "week" | "month"; + }>({}); + const [paymentModal, setPaymentModal] = useState(false); + const [advancedSelector, setSelector] = useState(false); + + const { + register, + handleSubmit, + formState: { errors }, + setValue, + watch, + } = useForm({ + resolver: zodResolver(CampaignSchemas.create), + defaultValues: { + recipients: [], + ...templates.blank, + style: "PLUNK", + }, + }); + + if (!project || !events) { + return ; + } + + const selectQuery = () => { + if (!contacts) { + return; + } + + let filteredContacts = contacts.contacts; + + if (query.events && query.events.length > 0) { + query.events.map((e) => { + filteredContacts = filteredContacts.filter((c) => + c.triggers.some((t) => t.eventId === e), + ); + }); + } + + if (query.last) { + filteredContacts = filteredContacts.filter((c) => { + if (c.triggers.length === 0) { + return false; + } + + const lastTrigger = c.triggers.sort((a, b) => + a.createdAt > b.createdAt ? -1 : 1, + ); + + if (lastTrigger.length === 0) { + return false; + } + + return dayjs(lastTrigger[0].createdAt).isAfter( + dayjs().subtract(1, query.last), + ); + }); + } + + if (query.notevents && query.notevents.length > 0 && query.notlast) { + query.notevents.map((e) => { + filteredContacts = filteredContacts.filter((c) => { + if (c.triggers.length === 0) { + return true; + } + + const lastTrigger = c.triggers + .filter((t) => t.eventId === e) + .sort((a, b) => (a.createdAt > b.createdAt ? -1 : 1)); + + if (lastTrigger.length === 0) { + return true; + } + + return dayjs(lastTrigger[0].createdAt).isAfter( + dayjs().subtract(1, query.last), + ); + }); + }); + } else if (query.notevents && query.notevents.length > 0) { + query.notevents.map((e) => { + filteredContacts = filteredContacts.filter((c) => + c.triggers.every((t) => t.eventId !== e), + ); + }); + } else if (query.notlast) { + filteredContacts = filteredContacts.filter((c) => { + if (c.triggers.length === 0) { + return true; + } + + const lastTrigger = c.triggers.sort((a, b) => + a.createdAt > b.createdAt ? -1 : 1, + ); + + if (lastTrigger.length === 0) { + return true; + } + + return !dayjs(lastTrigger[0].createdAt).isAfter( + dayjs().subtract(1, query.notlast), + ); + }); + } + + if (query.data) { + filteredContacts = filteredContacts.filter((c) => { + if (!c.data) { + return false; + } + + return JSON.parse(c.data)[query.data as string]; + }); + } + + if (query.data && query.value) { + filteredContacts = filteredContacts.filter((c) => { + if (!c.data) { + return false; + } + + return Array.isArray(JSON.parse(c.data)[query.data as string]) + ? JSON.parse(c.data)[query.data as string].includes(query.value) + : JSON.parse(c.data)[query.data as string] === query.value; + }); + } + + setValue( + "recipients", + filteredContacts.map((c) => c.id), + ); + }; + + const create = async (data: CampaignValues) => { + toast.promise( + network.mock( + project.secret, + "POST", + "/v1/campaigns", + data.recipients.length === + contacts?.contacts.filter((c) => c.subscribed).length + ? { ...data, recipients: ["all"] } + : { + ...data, + }, + ), + { + loading: "Creating new campaign", + success: () => { + void mutate(); + return "Created new campaign"; + }, + error: "Could not create new campaign!", + }, + ); + + await router.push("/campaigns"); + }; + + return ( + <> + + +
    + + {contacts ? ( + <> +
    + + setValue("recipients", c)} + values={contacts.contacts + .filter((c) => c.subscribed) + .map((c) => { + return { name: c.email, value: c.id }; + })} + selectedValues={watch("recipients")} + /> + + {(errors.recipients as FieldError | undefined)?.message && ( + + {(errors.recipients as FieldError | undefined)?.message} + + )} + +
    + +
    + + +
    + + + {advancedSelector && ( + +
    + + + setQuery( + e.length > 0 + ? { ...query, events: e } + : { + ...query, + events: undefined, + last: undefined, + }, + ) + } + values={[ + ...events + .filter((e) => !query.notevents?.includes(e.id)) + .sort((a, b) => { + if (!a.templateId && !a.campaignId) { + return -1; + } + if (!b.templateId && !b.campaignId) { + return 1; + } + + if ( + a.name.includes("delivered") && + !b.name.includes("delivered") + ) { + return -1; + } + + return 0; + }) + .map((e) => { + return { + name: e.name, + value: e.id, + tag: + e.templateId ?? e.campaignId + ? e.name.includes("opened") + ? "On Open" + : "On Delivery" + : undefined, + }; + }), + ]} + selectedValues={query.events ?? []} + /> +
    + +
    + {query.events && query.events.length > 0 && ( + <> + + + setQuery({ + ...query, + last: + (e as "" | "day" | "week" | "month") === "" + ? undefined + : (e as "day" | "week" | "month"), + }) + } + values={[ + { name: "Anytime", value: "" }, + { name: "In the last day", value: "day" }, + { name: "In the last week", value: "week" }, + { name: "In the last month", value: "month" }, + ]} + selectedValue={query.last ?? ""} + /> + + )} +
    + +
    + + { + setQuery( + e.length > 0 + ? { ...query, notevents: e } + : { + ...query, + notevents: undefined, + notlast: undefined, + }, + ); + }} + values={[ + ...events + .filter((e) => !query.events?.includes(e.id)) + .sort((a, b) => { + if (!a.templateId && !a.campaignId) { + return -1; + } + if (!b.templateId && !b.campaignId) { + return 1; + } + + if ( + a.name.includes("delivered") && + !b.name.includes("delivered") + ) { + return -1; + } + + return 0; + }) + .map((e) => { + return { + name: e.name, + value: e.id, + tag: + e.templateId ?? e.campaignId + ? e.name.includes("opened") + ? "On Open" + : "On Delivery" + : undefined, + }; + }), + ]} + selectedValues={query.notevents ?? []} + /> +
    + +
    + {query.notevents && query.notevents.length > 0 && ( + <> + + + setQuery({ + ...query, + notlast: + (e as "" | "day" | "week" | "month") === "" + ? undefined + : (e as "day" | "week" | "month"), + }) + } + values={[ + { name: "Anytime", value: "" }, + { name: "In the last day", value: "day" }, + { name: "In the last week", value: "week" }, + { name: "In the last month", value: "month" }, + ]} + selectedValue={query.notlast ?? ""} + /> + + )} +
    + +
    + + + setQuery({ + ...query, + data: e === "" ? undefined : e, + }) + } + values={[ + { name: "Any parameter", value: "" }, + ...new Set( + contacts.contacts + .filter((c) => c.data) + .map((c) => { + return Object.keys( + JSON.parse(c.data ?? "{}"), + ); + }) + .reduce((acc, val) => acc.concat(val), []), + ), + ].map((k) => + typeof k === "string" ? { name: k, value: k } : k, + )} + selectedValue={query.data ?? ""} + /> +
    + +
    + {query.data && ( + <> + + + + setQuery({ + ...query, + value: e === "" ? undefined : e, + }) + } + values={[ + { name: "Any value", value: "" }, + ...new Set( + contacts.contacts + .filter( + (c) => + c.data && + JSON.parse(c.data)[query.data ?? ""], + ) + .map((c) => { + return JSON.parse(c.data ?? "{}")[ + query.data ?? "" + ]; + }) + .reduce((acc, val) => acc.concat(val), []), + ), + ].map((k) => + typeof k === "string" + ? { + name: k, + value: k, + } + : (k as { + name: string; + value: string; + }), + )} + selectedValue={query.value ?? ""} + /> + + )} +
    + +
    + { + e.preventDefault(); + selectQuery(); + }} + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.9 }} + className={ + "ml-auto flex items-center justify-center gap-x-0.5 rounded bg-neutral-800 px-8 py-2 text-center text-sm font-medium text-white" + } + > + + + + + Select contacts + +
    +
    + )} +
    + + ) : ( + <> +
    + +
    +

    + Hang on! +

    +

    + We're still loading your contacts. This might take up to a + minute. You can already start writing your campaign in the + editor below. +

    +
    +
    + + )} + + + {watch("recipients").length >= 10 && ( + + + Your campaign will be sent out in batches of 80 recipients + each. It will be delivered to all contacts{" "} + {dayjs().to( + dayjs().add( + Math.ceil(watch("recipients").length / 80), + "minutes", + ), + )} + + + )} + + +
    + { + setValue("body", value); + setValue("style", type); + }} + modeSwitcher + /> + + {errors.body?.message && ( + + {errors.body.message} + + )} + +
    + +
    + { + e.preventDefault(); + return router.push("/campaigns"); + }} + className={ + "flex w-full justify-center rounded border border-neutral-300 bg-white px-6 py-2 text-base font-medium text-neutral-800 focus:outline-none focus:ring-2 focus:ring-neutral-800 focus:ring-offset-2 sm:mt-0 sm:w-auto sm:text-sm" + } + > + Cancel + + + + + + + + Create + +
    + +
    +
    + + ); +} diff --git a/packages/dashboard/src/pages/contacts/[id].tsx b/packages/dashboard/src/pages/contacts/[id].tsx new file mode 100644 index 0000000..7169df9 --- /dev/null +++ b/packages/dashboard/src/pages/contacts/[id].tsx @@ -0,0 +1,777 @@ +// @ts-nocheck +// React Hook Form messes up our types, ignore the entire file + +import { zodResolver } from "@hookform/resolvers/zod"; +import { + ContactSchemas, + EventSchemas, + type UtilitySchemas, +} from "@plunk/shared"; +import type { Contact, Email, Template } from "@prisma/client"; +import dayjs from "dayjs"; +import { motion } from "framer-motion"; +import { Save } from "lucide-react"; +import { useRouter } from "next/router"; +import React, { useEffect, useState } from "react"; +import { useFieldArray, useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; +import { + Card, + Empty, + FullscreenLoader, + Input, + Modal, + Toggle, +} from "../../components"; +import { Dashboard } from "../../layouts"; +import { useContact } from "../../lib/hooks/contacts"; +import { useActiveProject } from "../../lib/hooks/projects"; +import { network } from "../../lib/network"; + +interface ContactValues { + email: string; + data: string | null; + subscribed: boolean; +} + +interface EventValues { + event: string; +} + +/** + * + */ +export default function Index() { + const router = useRouter(); + + if (!router.isReady) { + return ; + } + + const [eventModal, setEventModal] = useState(false); + + const project = useActiveProject(); + const { data: contact, mutate } = useContact({ + id: router.query.id as string, + }); + + const { handleSubmit, watch, setValue, reset } = useForm({ + resolver: zodResolver(ContactSchemas.update), + }); + + const { + register: dataRegister, + control, + getValues: getDataValues, + reset: dataReset, + } = useForm({ + defaultValues: { + data: Object.entries(JSON.parse(contact?.data ? contact.data : "{}")).map( + ([key]) => ({ + value: { key }, + }), + ), + }, + resolver: zodResolver( + z.object({ + data: z + .array( + z.object({ + value: z.object({ key: z.string(), value: z.string() }), + }), + ) + .min(0), + }), + ), + }); + + const { + fields, + append: fieldAppend, + remove: fieldRemove, + } = useFieldArray({ control, name: "data" }); + + const { + register: eventRegister, + handleSubmit: eventHandleSubmit, + formState: { errors: eventErrors }, + reset: eventReset, + } = useForm({ + resolver: zodResolver(EventSchemas.post.pick({ event: true })), + }); + + useEffect(() => { + if (!contact) { + return; + } + + reset(contact); + dataReset({ + data: Object.entries(JSON.parse(contact.data ? contact.data : "{}")).map( + ([key, value]) => ({ + value: { key, value }, + }), + ), + }); + }, [dataReset, reset, contact]); + + if (!contact) { + return ; + } + + const create = (data: EventValues) => { + toast.promise( + network.mock( + project.secret, + "POST", + "/v1", + { + ...data, + email: contact.email, + }, + ), + { + loading: "Creating new event", + success: () => { + void mutate(); + eventReset(); + return "Created new event"; + }, + error: "Could not create new event!", + }, + ); + + setEventModal(false); + }; + + const update = (data: ContactValues) => { + const entries = getDataValues().data.map(({ value }) => [ + value.key, + value.value, + ]); + let dataObject = {}; + + entries.forEach(([key, value]) => { + Object.assign(dataObject, { [key]: value }); + }); + + dataObject = Object.fromEntries( + Object.entries(dataObject).filter(([, value]) => value !== ""), + ); + + toast.promise( + network.mock( + project.secret, + "PUT", + "/v1/contacts", + { + id: contact.id, + ...data, + data: dataObject, + }, + ), + { + loading: "Saving your changes", + success: () => { + void mutate(); + return "Saved your changes"; + }, + error: "Could not save your changes!", + }, + ); + }; + + const remove = async (e: { preventDefault: () => void }) => { + e.preventDefault(); + toast.promise( + network.mock( + project.secret, + "DELETE", + "/v1/contacts", + { + id: contact.id, + }, + ), + { + loading: "Deleting contact", + success: "Deleted contact", + error: "Could not delete contact!", + }, + ); + + await router.push("/contacts"); + }; + + return ( + <> + setEventModal(!eventModal)} + onAction={eventHandleSubmit(create)} + type={"info"} + action={"Trigger"} + title={"Trigger event"} + description={`Trigger an event for ${contact.email}`} + icon={ + <> + + + + } + > + + + + + + + + } + > +
    +
    + + + {contact.email[0].toUpperCase()} + + +

    + {contact.email[0].toUpperCase()} + {contact.email.slice(1)} +

    +
    + +
    +
    + + +
    + + {fields.length > 0 ? ( + fields.map((field, index) => { + return ( + <> +
    +
    +
    + + +
    + +
    + + +
    + +
    +
    + + ); + }) + ) : ( +

    No fields added

    + )} +
    + +
    + setValue("subscribed", !watch("subscribed"))} + /> +
    + +
    + + + Save + +
    + +
    + + {contact.triggers.length > 0 || contact.emails.length > 0 ? ( +
    +
      + {[...contact.triggers, ...contact.emails] + .sort( + (a, b) => + new Date(b.createdAt).getTime() - + new Date(a.createdAt).getTime(), + ) + .map((t, index) => { + if (t.messageId) { + const email = t as Email; + + return ( +
    • +
      + {contact.triggers.length + + contact.emails.length - + 1 !== + index && ( +
      +
    • + ); + } + + if (t.action) { + return ( +
    • +
      + {contact.triggers.length + + contact.emails.length - + 1 !== + index && ( +
      +
    • + ); + } + + if (t.event) { + return ( +
    • +
      + {contact.triggers.length + + contact.emails.length - + 1 !== + index && ( +
      +
    • + ); + } + })} +
    +
    + ) : ( + + )} +
    +
    + + ); +} diff --git a/packages/dashboard/src/pages/contacts/index.tsx b/packages/dashboard/src/pages/contacts/index.tsx new file mode 100644 index 0000000..529f998 --- /dev/null +++ b/packages/dashboard/src/pages/contacts/index.tsx @@ -0,0 +1,540 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { ContactSchemas } from "@plunk/shared"; +import type { Template } from "@prisma/client"; +import dayjs from "dayjs"; +import { AnimatePresence, motion } from "framer-motion"; +import { Edit2, Plus } from "lucide-react"; +import Link from "next/link"; +import React, { useState } from "react"; +import { type FieldError, useFieldArray, useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { z } from "zod"; +import { + Card, + Empty, + FullscreenLoader, + Modal, + Skeleton, + Table, + Toggle, +} from "../../components"; +import { Dashboard } from "../../layouts"; +import { searchContacts, useContacts } from "../../lib/hooks/contacts"; +import { useActiveProject } from "../../lib/hooks/projects"; +import { useUser } from "../../lib/hooks/users"; +import { network } from "../../lib/network"; + +interface ContactValues { + email: string; + data?: + | undefined + | { + [x: string]: string | string[]; + } + | null + | undefined; + subscribed: boolean; +} + +/** + * + */ +export default function Index() { + const [page, setPage] = useState(1); + const [query, setQuery] = useState(); + + const project = useActiveProject(); + const { data: user } = useUser(); + const { data: contacts, mutate } = useContacts(page); + const { data: search } = searchContacts(query); + + const [contactModal, setContactModal] = useState(false); + + const { + register, + handleSubmit, + formState: { errors }, + reset, + watch, + setValue, + } = useForm({ + resolver: zodResolver(ContactSchemas.create), + defaultValues: { + subscribed: true, + }, + }); + + const { + register: dataRegister, + control, + getValues: getDataValues, + reset: dataReset, + } = useForm({ + resolver: zodResolver( + z.object({ + data: z + .array( + z.object({ + value: z.object({ key: z.string(), value: z.string() }), + }), + ) + .min(0), + }), + ), + defaultValues: { + data: [{ value: { key: "", value: "" } }], + }, + }); + + const { + fields, + append: fieldAppend, + remove: fieldRemove, + } = useFieldArray({ control, name: "data" }); + + if (!project || !user) { + return ; + } + + const create = (data: ContactValues) => { + const entries = getDataValues().data.map(({ value }) => [ + value.key, + value.value, + ]); + let dataObject = {}; + + entries.forEach(([key, value]) => { + Object.assign(dataObject, { [key]: value }); + }); + + dataObject = Object.fromEntries( + Object.entries(dataObject).filter(([, value]) => value !== ""), + ); + + toast.promise( + network.mock( + project.secret, + "POST", + "/v1/contacts", + { + ...data, + subscribed: true, + data: dataObject, + }, + ), + { + loading: "Creating new contact", + success: () => { + void mutate(); + reset(); + return "Created new contacts"; + }, + error: "Could not create new contact!", + }, + ); + + reset(); + dataReset(); + + setContactModal(false); + }; + + const renderContacts = () => { + if (!contacts && !search) { + return ; + } + + if (query && !search) { + return ; + } + + if (search && query !== undefined) { + if (search.contacts.length > 0) { + return ( + <> +
    { + const aTrigger = + a.triggers.length > 0 + ? a.triggers.sort()[0].createdAt + : a.createdAt; + + const bTrigger = + b.triggers.length > 0 + ? b.triggers.sort()[0].createdAt + : b.createdAt; + + return bTrigger > aTrigger ? 1 : -1; + }) + .map((u) => { + return { + Email: u.email, + "Last Activity": dayjs() + .to( + [...u.triggers, ...u.emails].length > 0 + ? [...u.triggers, ...u.emails].sort((a, b) => { + return a.createdAt > b.createdAt ? -1 : 1; + })[0].createdAt + : u.createdAt, + ) + .toString(), + Subscribed: u.subscribed, + Edit: ( + + + + ), + }; + })} + /> + + ); + } + return ( + <> + + + + } + title={"No contacts found"} + description={`Your query ${query} did not return any contacts`} + /> + + ); + } + + if (contacts) { + if (contacts.contacts.length > 0) { + return ( + <> +
    { + const aTrigger = + a.triggers.length > 0 + ? a.triggers.sort()[0].createdAt + : a.createdAt; + + const bTrigger = + b.triggers.length > 0 + ? b.triggers.sort()[0].createdAt + : b.createdAt; + + return bTrigger > aTrigger ? 1 : -1; + }) + .map((u) => { + return { + Email: u.email, + "Last Activity": dayjs() + .to( + u.triggers.length > 0 + ? u.triggers.sort((a, b) => { + return a.createdAt > b.createdAt ? -1 : 1; + })[0].createdAt + : u.createdAt, + ) + .toString(), + Subscribed: u.subscribed, + Edit: ( + + + + ), + }; + })} + /> + + + ); + } + return ( + <> + + + ); + } + }; + + return ( + <> + setContactModal(!contactModal)} + onAction={handleSubmit(create)} + type={"info"} + action={"Create"} + title={"Create new contact"} + > +
    + +
    + +
    + + {errors.email?.message && ( + + {errors.email.message} + + )} + +
    + +
    +
    +
    + + +
    + + {fields.length > 0 ? ( + fields.map((field, index) => { + // @ts-ignore + return ( + <> +
    +
    +
    + + +
    +
    + + +
    + +
    +
    + + ); + }) + ) : ( +

    No fields added

    + )} +
    + + {(errors.data as FieldError | undefined)?.message && ( + + {(errors.data as FieldError | undefined)?.message} + + )} + +
    + +
    + setValue("subscribed", !watch("subscribed"))} + /> +
    +
    + + + setQuery(e.target.value)} + autoComplete={"off"} + type="search" + placeholder={"Search email or metadata"} + className={ + "rounded border-neutral-300 transition ease-in-out focus:border-neutral-800 focus:ring-neutral-800 sm:text-sm" + } + /> + + setContactModal(true)} + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.9 }} + className={ + "flex items-center justify-center gap-x-1 rounded bg-neutral-800 px-8 py-2 text-center text-sm font-medium text-white" + } + > + + New + + + } + > + {renderContacts()} + + + + ); +} diff --git a/packages/dashboard/src/pages/events/index.tsx b/packages/dashboard/src/pages/events/index.tsx new file mode 100644 index 0000000..0b00177 --- /dev/null +++ b/packages/dashboard/src/pages/events/index.tsx @@ -0,0 +1,432 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { EventSchemas, type UtilitySchemas } from "@plunk/shared"; +import type { Template } from "@prisma/client"; +import dayjs from "dayjs"; +import { motion } from "framer-motion"; +import { Plus, TerminalSquare, Trash } from "lucide-react"; +import Link from "next/link"; +import React, { useState } from "react"; +import { useForm } from "react-hook-form"; +import { Area, AreaChart, ResponsiveContainer, YAxis } from "recharts"; +import { toast } from "sonner"; +import { + Alert, + Badge, + Card, + Empty, + FullscreenLoader, + Input, + Modal, + Skeleton, + Table, +} from "../../components"; +import { Dashboard } from "../../layouts"; +import { useContactsCount } from "../../lib/hooks/contacts"; +import { useEvents } from "../../lib/hooks/events"; +import { useActiveProject } from "../../lib/hooks/projects"; +import { useUser } from "../../lib/hooks/users"; +import { network } from "../../lib/network"; + +interface EventValues { + event: string; +} + +/** + * + */ +export default function Index() { + const project = useActiveProject(); + const { data: user } = useUser(); + const { data: contacts } = useContactsCount(); + const { data: events, mutate } = useEvents(); + + const [eventModal, setEventModal] = useState(false); + + const { + register, + handleSubmit, + formState: { errors }, + reset, + } = useForm({ + resolver: zodResolver(EventSchemas.post.pick({ event: true })), + }); + + if (!project || !user) { + return ; + } + + const create = (data: EventValues) => { + toast.promise( + network.mock( + project.secret, + "POST", + "/v1", + { + ...data, + email: user.email, + subscribed: true, + }, + ), + { + loading: "Creating new event", + success: () => { + void mutate(); + reset(); + return "Created new event"; + }, + error: "Could not create new event!", + }, + ); + + setEventModal(false); + }; + + const remove = (id: string) => { + toast.promise( + network.mock( + project.secret, + "DELETE", + "/v1/events", + { + id, + }, + ), + { + loading: "Deleting your event", + success: () => { + void mutate(); + return "Deleted your event"; + }, + error: "Could not delete your event!", + }, + ); + }; + + return ( + <> + setEventModal(!eventModal)} + onAction={handleSubmit(create)} + type={"info"} + action={"Trigger"} + title={"Create a new event"} + description={"Trigger a new event to send out emails to your contacts"} + icon={ + <> + + + + } + > + + + + + {events?.length === 0 && ( + +
    +

    + Want us to help you get started? We can help you build your + first action in less than 5 minutes. +

    + + + Build an action + +
    +
    + )} + + + setEventModal(true)} + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.9 }} + className={ + "flex items-center gap-x-1 rounded bg-neutral-800 px-8 py-2 text-center text-sm font-medium text-white" + } + > + + New + + + } + > + {events && contacts ? ( + events.filter((event) => !event.templateId && !event.campaignId) + .length > 0 ? ( +
    !event.templateId && !event.campaignId) + .sort((a, b) => { + const aTrigger = + a.triggers.length > 0 + ? a.triggers.sort()[0].createdAt + : a.createdAt; + + const bTrigger = + b.triggers.length > 0 + ? b.triggers.sort()[0].createdAt + : b.createdAt; + + return bTrigger > aTrigger ? 1 : -1; + }) + .map((e) => { + return { + Event: e.name, + "Triggered by users": ( + {`${ + e.triggers.length > 0 + ? Math.round( + ([ + ...new Map( + e.triggers.map((t) => [t.contactId, t]), + ).values(), + ].length / + contacts) * + 100, + ) + : 0 + }%`} + ), + "Total triggers": e.triggers.length, + Timeline: ( + <> + + { + const date = dayjs(cur.createdAt).format( + "MM/YYYY", + ); + + if (acc[date]) { + acc[date] += 1; + } else { + acc[date] = 1; + } + + return acc; + }, + {} as Record, + ), + ) + .sort((a, b) => { + // day is the month with year e.g 01/2021 + const aDay = a[0]; + const bDay = b[0]; + + return aDay > bDay ? 1 : -1; + }) + .map(([day, count]) => { + return { + day, + count, + }; + })} + margin={{ + top: 5, + right: 0, + left: 0, + }} + > + + + + + + + + + + + + + + ), + "Last Activity": dayjs() + .to( + e.triggers.length > 0 + ? e.triggers.sort((a, b) => { + return b.createdAt > a.createdAt ? 1 : -1; + })[0].createdAt + : e.createdAt, + ) + .toString(), + Trigger: ( + + ), + + Remove: !e.templateId ? ( + + ) : ( + Cannot be deleted + ), + }; + })} + /> + ) : ( + + ) + ) : ( + + )} + + + {events && contacts ? ( + events.filter((event) => event.templateId).length > 0 ? ( +
    event.templateId) + .sort((a, b) => { + const aTrigger = + a.triggers.length > 0 + ? a.triggers.sort()[0].createdAt + : a.createdAt; + + const bTrigger = + b.triggers.length > 0 + ? b.triggers.sort()[0].createdAt + : b.createdAt; + + return bTrigger > aTrigger ? 1 : -1; + }) + .map((e) => { + return { + Event: e.name, + "Triggered by users": ( + {`${ + e.triggers.length > 0 + ? Math.round( + ([ + ...new Map( + e.triggers.map((t) => [t.contactId, t]), + ).values(), + ].length / + contacts) * + 100, + ) + : 0 + }%`} + ), + "Total times triggered": e.triggers.length, + "Last Activity": dayjs() + .to( + e.triggers.length > 0 + ? e.triggers.sort((a, b) => { + return b.createdAt > a.createdAt ? 1 : -1; + })[0].createdAt + : e.createdAt, + ) + .toString(), + }; + })} + /> + ) : ( + + ) + ) : ( + + )} + + + + ); +} diff --git a/packages/dashboard/src/pages/index.tsx b/packages/dashboard/src/pages/index.tsx new file mode 100644 index 0000000..d834d4b --- /dev/null +++ b/packages/dashboard/src/pages/index.tsx @@ -0,0 +1,300 @@ +import dayjs from "dayjs"; +import { Book, Eye, Frown, LineChart, Send } from "lucide-react"; +import Link from "next/link"; +import React, { useState } from "react"; +import { + Badge, + Card, + Empty, + FullscreenLoader, + Redirect, + Skeleton, + Table, +} from "../components"; +import { Dashboard } from "../layouts"; +import { + useActiveProject, + useActiveProjectFeed, + useProjects, +} from "../lib/hooks/projects"; + +/** + * + */ +export default function Index() { + const [feedPage, setFeedPage] = useState(1); + + const activeProject = useActiveProject(); + const { data: projects } = useProjects(); + const { data: feed } = useActiveProjectFeed(feedPage); + + if (projects?.length === 0) { + return ; + } + + if (!activeProject) { + return ; + } + + return ( + <> + + <> +
    +
    + {activeProject.verified ? ( + <> +
    + + + +
    +
    +

    + +

    +

    + Send a broadcast to your contacts +

    +
    + + ) : ( + <> +
    + + + +
    +
    + Important +

    + +

    +

    + Verify your domain before you send emails +

    +
    + + )} + + +
    + +
    +
    + + + +
    +
    +

    + +

    +

    + Discover insights about your emails +

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

    + + +

    +

    + Discover how to use Plunk +

    +
    + +
    +
    + + + {feed ? ( + feed.length === 0 ? ( + <> + } + title={"No feed yet"} + description={ + "Send an email or track an event to see it here" + } + /> + + ) : ( + <> +
    { + if ("messageId" in f) { + return { + Email: f.contact.email, + Activity: ( + + {f.createdAt === f.updatedAt + ? "Email delivered" + : `Email ${f.status.toLowerCase()}`} + + ), + Type: Email, + Time: dayjs().to(dayjs(f.createdAt)), + View: ( + + + + ), + }; + } + if (f.action) { + return { + Email: f.contact.email, + Activity: ( + {f.action.name} + ), + Type: Action, + Time: dayjs().to(dayjs(f.createdAt)), + View: ( + + + + ), + }; + } + if (f.event) { + return { + Email: f.contact.email, + Activity: {f.event.name}, + Type: Event, + Time: dayjs().to(dayjs(f.createdAt)), + View: ( + + + + ), + }; + } + + return {}; + })} + /> + + + + ) + ) : ( + + )} + + + + + ); +} diff --git a/packages/dashboard/src/pages/manage/[id].tsx b/packages/dashboard/src/pages/manage/[id].tsx new file mode 100644 index 0000000..f5b7a48 --- /dev/null +++ b/packages/dashboard/src/pages/manage/[id].tsx @@ -0,0 +1,138 @@ +import type { UtilitySchemas } from "@plunk/shared"; +import type { User } from "@prisma/client"; +import { motion } from "framer-motion"; +import { NextSeo } from "next-seo"; +import { useRouter } from "next/router"; +import React, { useState } from "react"; +import { toast } from "sonner"; +import { FullscreenLoader, Redirect } from "../../components"; +import { useContact } from "../../lib/hooks/contacts"; +import { network } from "../../lib/network"; + +/** + * + */ +export default function Index() { + const router = useRouter(); + + if (!router.isReady) { + return ; + } + + const { + data: contact, + error, + mutate, + } = useContact({ id: router.query.id as string, withProject: true }); + const [submitted, setSubmitted] = useState(false); + + if (error) { + return ; + } + + if (!contact) { + return ; + } + + const update = () => { + setSubmitted(true); + + toast.promise( + network.mock( + contact.project.public, + "POST", + `/v1/contacts/${contact.subscribed ? "unsubscribe" : "subscribe"}`, + { + id: contact.id, + }, + ), + { + loading: "Updating your preferences", + success: () => { + void mutate(); + return "Updated your preferences"; + }, + error: "Could not update your preferences!", + }, + ); + + setSubmitted(false); + }; + + return ( + <> + +
    +
    +

    + {contact.subscribed ? "Unsubscribe from" : "Subscribe to"}{" "} + {contact.project.name} +

    +

    + {contact.subscribed + ? `You will no longer receive emails from ${contact.project.name} on ${contact.email} when you confirm that you want to unsubscribe.` + : `By confirming your subscription to ${contact.project.name} for ${contact.email} you agree to receive emails from us.`} +

    +
    + + {submitted ? ( + + + + + ) : ( + `${contact.subscribed ? "Unsubscribe" : "Subscribe"}` + )} + +
    +
    +
    + + ); +} diff --git a/packages/dashboard/src/pages/new.tsx b/packages/dashboard/src/pages/new.tsx new file mode 100644 index 0000000..ed0958a --- /dev/null +++ b/packages/dashboard/src/pages/new.tsx @@ -0,0 +1,264 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { ProjectSchemas } from "@plunk/shared"; +import type { Project } from "@prisma/client"; +import { AnimatePresence, motion } from "framer-motion"; +import { useAtom } from "jotai"; +import Link from "next/link"; +import { useRouter } from "next/router"; +import React, { useState } from "react"; +import { useForm, useFormState } from "react-hook-form"; +import Shared from "../../public/assets/shared.svg"; +import { FullscreenLoader, Redirect } from "../components"; +import { atomActiveProject } from "../lib/atoms/project"; +import { useProjects } from "../lib/hooks/projects"; +import { useUser } from "../lib/hooks/users"; +import { network } from "../lib/network"; + +interface ProjectValues { + name: string; + url: string; + error: string; +} + +/** + * + */ +export default function Index() { + const router = useRouter(); + + const [, setActiveProjectId] = useAtom(atomActiveProject); + + const { data: user, error } = useUser(); + const { data: projects, mutate } = useProjects(); + + const [submitted, setSubmitted] = useState(false); + + const { + register, + handleSubmit, + formState: { errors }, + setError, + control, + } = useForm({ + resolver: zodResolver(ProjectSchemas.create), + }); + + const { isValid } = useFormState({ + control, + }); + + if (error) { + return ; + } + + if (!user || !projects) { + return ; + } + + const create = async (data: ProjectValues) => { + setSubmitted(true); + + localStorage.removeItem("skip_onboarding"); + + const result = await network.fetch< + | { + data: Project; + success: true; + } + | { + success: false; + data: string; + }, + typeof ProjectSchemas.create + >("POST", "/projects/create", { + ...data, + url: data.url.startsWith("http") ? data.url : `https://${data.url}`, + }); + + if (result.success) { + await mutate([...projects, result.data]); + localStorage.setItem("project", result.data.id); + setActiveProjectId(result.data.id); + return router.push("/"); + } + setSubmitted(false); + setError("error", { message: result.data }); + }; + + return ( + <> +
    +
    +
    +
    +

    + Create a new project +

    +

    + Get ready to take your emails to the next level. +

    +
    + +
    +
    +
    +
    + + + + {errors.name?.message && ( + + {errors.name.message} + + )} + +
    + +
    + +
    + + https:// + + +
    + + + {errors.url?.message && ( + + {errors.url.message} + + )} + +
    + + + {errors.error?.message && ( + + {errors.error.message} + + )} + + + {submitted ? ( + + + + + ) : ( + + + + + + + Launch + + )} + + + + {projects.length > 0 ? ( +
    + + Back to the dashboard + +
    + ) : null} +
    +
    +
    +
    +
    +
    + +
    +
    +
    + + ); +} diff --git a/packages/dashboard/src/pages/onboarding/actions.tsx b/packages/dashboard/src/pages/onboarding/actions.tsx new file mode 100644 index 0000000..7fea943 --- /dev/null +++ b/packages/dashboard/src/pages/onboarding/actions.tsx @@ -0,0 +1,1057 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { ActionSchemas, EventSchemas, TemplateSchemas } from "@plunk/shared"; +import type { Template } from "@prisma/client"; +import { AnimatePresence, motion } from "framer-motion"; +import { useRouter } from "next/router"; +import React, { useEffect, useState } from "react"; +import { type FieldError, useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { + CodeBlock, + Dropdown, + Editor, + FullscreenLoader, + Modal, + MultiselectDropdown, + Toggle, + Tooltip, +} from "../../components"; +import { API_URI } from "../../lib/constants"; +import { useActions } from "../../lib/hooks/actions"; +import { useEvents } from "../../lib/hooks/events"; +import { useActiveProject } from "../../lib/hooks/projects"; +import { useTemplates } from "../../lib/hooks/templates"; +import { useUser } from "../../lib/hooks/users"; +import { network } from "../../lib/network"; + +interface EventValues { + event: string; +} + +interface TemplateValues { + subject: string; + body: string; + type: "MARKETING" | "TRANSACTIONAL"; + style: "PLUNK" | "HTML"; +} + +interface ActionValues { + name: string; + runOnce: boolean; + delay: number; + template: string; + events: string[]; + notevents: string[]; +} + +/** + * + */ +export default function Index() { + const router = useRouter(); + + const activeProject = useActiveProject(); + const { data: user } = useUser(); + const { data: events, mutate: eventMutate } = useEvents(); + const { data: templates, mutate: templateMutate } = useTemplates(); + const { data: actions, mutate: actionMutate } = useActions(); + + const [eventModal, setEventModal] = useState(false); + const [advancedSettings, setAdvancedSettings] = useState(false); + const [step, setStep] = useState<0 | 1 | 2 | 3>(0); + + const [language, setLanguage] = useState< + "javascript" | "python" | "curl" | "PHP" | "ruby" + >("curl"); + const [delay, setDelay] = useState<{ + delay: number; + unit: "MINUTES" | "HOURS" | "DAYS"; + }>({ + delay: 0, + unit: "MINUTES", + }); + + useEffect(() => { + switch (delay.unit) { + case "MINUTES": + actionSetValue("delay", delay.delay); + break; + case "HOURS": + actionSetValue("delay", delay.delay * 60); + break; + case "DAYS": + actionSetValue("delay", delay.delay * 24 * 60); + break; + } + }, [delay]); + + const { + register: eventRegister, + handleSubmit: eventHandleSubmit, + formState: { errors: eventErrors }, + getValues: eventGetValues, + } = useForm({ + resolver: zodResolver(EventSchemas.post.pick({ event: true })), + }); + + const { + register: templateRegister, + handleSubmit: templateHandleSubmit, + formState: { errors: templateErrors }, + setValue: templateSetValue, + watch: templateWatch, + } = useForm({ + resolver: zodResolver(TemplateSchemas.create), + defaultValues: { + type: "MARKETING", + style: "PLUNK", + subject: "Welcome to Plunk!", + body: + '

    Welcome to Plunk!

    \n' + + "

    Writing emails in Plunk is super easy, anyone do it! \n" + + 'They also support all sorts of cool stuff like code blocks, bold text, and links.

    Highlight this text and see what is possible!

    ', + }, + }); + + const { + register: actionRegister, + handleSubmit: actionHandleSubmit, + formState: { errors: actionErrors }, + setValue: actionSetValue, + watch: actionWatch, + } = useForm({ + resolver: zodResolver(ActionSchemas.create), + defaultValues: { + template: "No template selected", + events: [], + notevents: [], + runOnce: false, + }, + }); + + if (!activeProject || !events || !user || !actions || !templates) { + return ; + } + + const triggerEvent = (data: EventValues) => { + toast.promise( + network.mock( + activeProject.secret, + "POST", + "/v1/track", + { + event: data.event, + email: user.email, + subscribed: true, + }, + ), + { + loading: "Sending your event", + success: () => { + setEventModal(false); + void eventMutate(); + + return `${data.event} delivered`; + }, + error: "Having trouble sending your event, we will try again later!", + }, + ); + }; + + const createTemplate = (data: TemplateValues) => { + toast.promise( + network.mock( + activeProject.secret, + "POST", + "/v1/templates", + { + ...data, + }, + ), + { + loading: "Creating new template", + success: () => { + void templateMutate(); + + setStep(3); + + return "Created your template"; + }, + error: "Could not create new email!", + }, + ); + }; + + const createAction = (data: ActionValues) => { + toast.promise( + Promise.all([ + fetch("/api/plunk", { + method: "POST", + body: JSON.stringify({ + event: "onboarding-completed", + email: user.email, + data: { + project: activeProject.name, + firstEvent: events.sort( + (a, b) => + new Date(a.createdAt).getTime() - + new Date(b.createdAt).getTime(), + )[0].name, + }, + }), + headers: { "Content-Type": "application/json" }, + }), + network.mock( + activeProject.secret, + "POST", + "/v1/actions", + { + ...data, + }, + ), + ]), + { + loading: "Creating new action", + success: () => { + void actionMutate(); + return "Created your action"; + }, + error: "Could not create new action!", + }, + ); + }; + + const renderStep = () => { + switch (step) { + case 0: + return ( + <> + + + 👋 + +

    Let's get started!

    +
    +

    Are you ready to give Plunk Actions a spin?

    + +

    + In this 3 step tutorial, we'll help you set up your first + email action so that you have an example on hand when you are + ready to start building your own. +

    +
    + + setStep(1)} + className={ + "mt-6 rounded bg-neutral-800 px-12 py-4 text-sm font-medium text-white" + } + > + Let's get started! + +
    + + ); + case 1: + return events.length > 0 ? ( + <> + + + 🎉 + +

    + Your event has successfully arrived +

    +

    + We have received your event{" "} + + { + events.sort( + (a, b) => + new Date(a.createdAt).getTime() - + new Date(b.createdAt).getTime(), + )[0].name + } + + , you are now ready to create your first email template! +

    + setStep(2)} + className={ + "mt-4 rounded-md bg-neutral-800 px-10 py-3 text-sm font-medium text-white" + } + > + Design an email + +
    + + ) : ( + <> + +
    +

    + Track your first event +

    +

    + Actions start from events. You can call them whatever you want + and send them from anywhere using an API call. +

    +
    +
    +
    +

    + From your application +

    +
    +
    + + setLanguage(e as "javascript" | "python" | "curl") + } + values={[ + { value: "curl", name: "cURL" }, + { name: "JavaScript", value: "javascript" }, + { value: "python", name: "Python" }, + { value: "PHP", name: "PHP" }, + { value: "ruby", name: "Ruby" }, + ]} + selectedValue={language} + /> +
    + 'Bearer ${activeProject.secret}', 'Content-Type' => 'application/json'], '{ + "event": "my-new-event", + "email": "${user.email}" +}'); +$res = $client->sendAsync($request)->wait();`, + + ruby: `require "uri" +require "json" +require "net/http" + +url = URI("${API_URI}/v1/track") + +https = Net::HTTP.new(url.host, url.port) +https.use_ssl = true + +request = Net::HTTP::Post.new(url) +request["Authorization"] = "Bearer ${activeProject.secret}" +request["Content-Type"] = "application/json" +request.body = JSON.dump({ + "event": "my-new-event", + "email": "${user.email}" +}) + +response = https.request(request)`, + }[language] + } + /> +
    +
    + +
    +

    + From Plunk +

    +
    + setEventModal(true)} + className={ + "mt-6 rounded-md bg-neutral-800 px-10 py-4 text-sm font-medium text-white" + } + > + Trigger a demo event + +
    +
    +
    +
    + + ); + case 2: + return ( + <> + +
    +

    Design an email

    +

    + Our templates are easy to write and automatically transformed + into HTML that email clients understand. +

    +
    + +
    +
    + +
    + +
    + + {templateErrors.subject?.message && ( + + {templateErrors.subject.message} + + )} + +
    + +
    + + + templateSetValue( + "type", + t as "MARKETING" | "TRANSACTIONAL", + ) + } + values={[ + { name: "Marketing", value: "MARKETING" }, + { name: "Transactional", value: "TRANSACTIONAL" }, + ]} + selectedValue={templateWatch("type")} + /> + + {templateErrors.type?.message && ( + + {templateErrors.type.message} + + )} + +
    + +
    + templateSetValue("body", val)} + /> + + {templateErrors.body?.message && ( + + {templateErrors.body.message} + + )} + +
    + + + + + + + Create + + +
    + + ); + case 3: + return actions.length > 0 ? ( + <> + + + 🏎 + +

    + Your action has been created +

    +

    + Users will now automatically start to receive emails when they + complete your event{" "} + + { + events.sort( + (a, b) => + new Date(a.createdAt).getTime() - + new Date(b.createdAt).getTime(), + )[0].name + } + + . There is loads more to discover in Plunk but let's try out + your action first! +

    + { + toast.promise( + network.mock( + activeProject.secret, + "POST", + "/v1/track", + { + event: eventGetValues("event"), + email: user.email, + }, + ), + { + loading: "Sending your event", + success: `${eventGetValues("event")} delivered`, + error: "Could not deliver your event!", + }, + ); + + await router.push("/"); + }} + > + Try it out! + +
    + + ) : ( + <> + +
    +

    + Creating your first action +

    +

    + Actions tie together events and templates, they automate your + email workflows. +

    +
    +
    +
    + +
    + +
    + + {actionErrors.name?.message && ( + + {actionErrors.name.message} + + )} + +
    + +
    + + actionSetValue("events", e)} + values={events.map((e) => { + return { name: e.name, value: e.id }; + })} + selectedValues={actionWatch("events")} + /> + + {(actionErrors.events as FieldError | undefined) + ?.message && ( + + { + (actionErrors.events as FieldError | undefined) + ?.message + } + + )} + +
    + +
    + + actionSetValue("template", t)} + values={templates.map((t) => { + return { name: t.subject, value: t.id }; + })} + selectedValue={actionWatch("template")} + /> + + {actionErrors.template?.message && ( + + {actionErrors.template.message} + + )} + +
    + + { +
    + +
    + } + + + {advancedSettings && ( + +
    + +
    +
    + + setDelay({ + ...delay, + delay: Number.parseInt(e.target.value), + }) + } + /> +
    +
    + + setDelay({ + ...delay, + unit: t as "MINUTES" | "HOURS" | "DAYS", + }) + } + values={[ + { name: "Minutes", value: "MINUTES" }, + { name: "Hours", value: "HOURS" }, + { name: "Days", value: "DAYS" }, + ]} + selectedValue={delay.unit} + /> +
    +
    +
    + +
    + + actionSetValue("runOnce", !actionWatch("runOnce")) + } + /> +
    +
    + )} +
    + + + + + + + Create + + +
    + + ); + } + }; + + return ( + <> + setEventModal(!eventModal)} + onAction={eventHandleSubmit(triggerEvent)} + type={"info"} + action={"Trigger"} + title={"Trigger an event"} + description={"Trigger an event to use in your actions"} + > +
    + +
    + +
    + + {eventErrors.event?.message && ( + + {eventErrors.event.message} + + )} + +
    +
    +
    +
    +
    + +
    +
    + {renderStep()} +
    +
    + {step === 0 && ( +
    + { + await router.push("/onboarding"); + }} + > + Go back + +
    + )} +
    + + ); +} diff --git a/packages/dashboard/src/pages/onboarding/index.tsx b/packages/dashboard/src/pages/onboarding/index.tsx new file mode 100644 index 0000000..89d9123 --- /dev/null +++ b/packages/dashboard/src/pages/onboarding/index.tsx @@ -0,0 +1,76 @@ +import React from 'react'; +import {motion} from 'framer-motion'; +import {useRouter} from 'next/router'; +import {TerminalSquare, Workflow} from 'lucide-react'; + +/** + * + */ +export default function Index() { + const router = useRouter(); + + return ( + <> +
    +
    +

    Pick your fighter

    + +

    + Don't worry! You can use both, but we recommend starting with one. +

    +
    +
    +
    +
    + +
    + +
    +

    Actions

    +

    Repeatable workflows that are triggered by your app

    +
    + + router.push('/onboarding/actions')} + whileHover={{scale: 1.05}} + whileTap={{scale: 0.9}} + className={ + 'flex items-center gap-x-0.5 rounded-md bg-neutral-800 px-10 py-2.5 text-center text-sm font-medium text-white sm:col-span-2' + } + > + Start with actions + +
    +
    +
    + +
    + +
    +

    Transactional

    +

    Emails sent with a single API call

    +
    + router.push('/onboarding/transactional')} + whileHover={{scale: 1.05}} + whileTap={{scale: 0.9}} + className={ + 'mx-auto flex items-center gap-x-0.5 rounded-md bg-neutral-800 px-10 py-2.5 text-center text-sm font-medium text-white sm:col-span-2' + } + > + Start with transactional + +
    +
    +
    + + ); +} diff --git a/packages/dashboard/src/pages/onboarding/transactional.tsx b/packages/dashboard/src/pages/onboarding/transactional.tsx new file mode 100644 index 0000000..cc767cd --- /dev/null +++ b/packages/dashboard/src/pages/onboarding/transactional.tsx @@ -0,0 +1,242 @@ +import type { EventSchemas } from "@plunk/shared"; +import { motion } from "framer-motion"; +import { useRouter } from "next/router"; +import React, { useState } from "react"; +import { toast } from "sonner"; +import { CodeBlock, Dropdown, FullscreenLoader } from "../../components"; +import { API_URI } from "../../lib/constants"; +import { useEmailsCount } from "../../lib/hooks/emails"; +import { useActiveProject } from "../../lib/hooks/projects"; +import { useUser } from "../../lib/hooks/users"; +import { network } from "../../lib/network"; + +/** + * + */ +export default function Index() { + const router = useRouter(); + const project = useActiveProject(); + const { data: user } = useUser(); + const { data: emails, mutate } = useEmailsCount(); + const [language, setLanguage] = useState< + "javascript" | "python" | "curl" | "PHP" | "ruby" + >("curl"); + + if (!project || !user || emails === undefined) { + return ; + } + + return ( + <> +
    +
    + {emails > 0 ? ( + <> + + + 🏎 + +

    Wasn't that easy?

    +

    + Just like that you've sent your first email with Plunk! +

    + { + await router.push("/"); + }} + > + Explore the rest of Plunk + +
    + + ) : ( + <> + +
    + + 👋 + +

    Send it!

    +
    +

    + Are you ready to send a transactional email with Plunk? +

    + +

    + Sending a transactional email is as easy as making a + single API call. +

    +
    +
    + +
    + + setLanguage(e as "javascript" | "python" | "curl") + } + values={[ + { value: "curl", name: "cURL" }, + { name: "JavaScript", value: "javascript" }, + { value: "python", name: "Python" }, + { value: "PHP", name: "PHP" }, + { value: "ruby", name: "Ruby" }, + ]} + selectedValue={language} + /> + + 'Bearer ${project.secret}', 'Content-Type' => 'application/json'], '{ + "subject": "Your first email", + "body": "Hello from Plunk!", + "to": "${user.email}", +}'); +$res = $client->sendAsync($request)->wait();`, + + ruby: `require "uri" +require "json" +require "net/http" + +url = URI("${API_URI}/v1/send") + +https = Net::HTTP.new(url.host, url.port) +https.use_ssl = true + +request = Net::HTTP::Post.new(url) +request["Authorization"] = "Bearer ${project.secret}" +request["Content-Type"] = "application/json" +request.body = JSON.dump({ + "subject": "Your first email", + "body": "Hello from Plunk!", + "to": "${user.email}", +}) + +response = https.request(request)`, + }[language] + } + /> +
    + { + toast.promise( + network.mock( + project.secret, + "POST", + "/v1/send", + { + subject: "Your first email", + body: "Hello from Plunk!", + to: user.email, + }, + ), + { + loading: "Sending the email", + success: () => { + void mutate(); + return `Sent! Check your inbox at ${user.email}`; + }, + error: "Could not send the email", + }, + ); + }} + > + Run this code + +
    + + )} +
    + +
    + { + await router.push("/onboarding"); + }} + > + Go back + +
    +
    + + ); +} diff --git a/packages/dashboard/src/pages/settings/account.tsx b/packages/dashboard/src/pages/settings/account.tsx new file mode 100644 index 0000000..7b8db56 --- /dev/null +++ b/packages/dashboard/src/pages/settings/account.tsx @@ -0,0 +1,42 @@ +import {Dashboard} from '../../layouts'; +import {Card, FullscreenLoader} from '../../components'; +import {useUser} from '../../lib/hooks/users'; +import React from 'react'; + +/** + * + */ +export default function Index() { + const {data: user} = useUser(); + + if (!user) { + return ; + } + + return ( + <> + + +
    +
    + + +
    +
    +
    +
    + + ); +} diff --git a/packages/dashboard/src/pages/settings/api.tsx b/packages/dashboard/src/pages/settings/api.tsx new file mode 100644 index 0000000..2c11876 --- /dev/null +++ b/packages/dashboard/src/pages/settings/api.tsx @@ -0,0 +1,143 @@ +import type { Project } from "@prisma/client"; +import React, { useState } from "react"; +import { Card, FullscreenLoader, Modal, SettingTabs } from "../../components"; +import { Dashboard } from "../../layouts"; +import { useActiveProject, useProjects } from "../../lib/hooks/projects"; +import { network } from "../../lib/network"; + +import { RefreshCw } from "lucide-react"; +import { toast } from "sonner"; + +/** + * + */ +export default function Index() { + const [showRegenerateModal, setShowRegenerateModal] = useState(false); + const [project, setProject] = useState(); + + const activeProject = useActiveProject(); + const { data: projects, mutate: projectMutate } = useProjects(); + + if (activeProject && !project) { + setProject(activeProject); + } + + if (!project || !projects) { + return ; + } + + if (!activeProject) { + return ; + } + + const regenerate = () => { + setShowRegenerateModal(!showRegenerateModal); + + toast.promise( + network + .fetch<{ + success: true; + project: Project; + }>("POST", `/projects/id/${project.id}/regenerate`) + .then(async (res) => { + await projectMutate( + [ + ...projects.filter((project) => { + return project.id !== res.project.id; + }), + res.project, + ], + false, + ); + }), + { + loading: "Regenerating API keys...", + success: "Successfully regenerated API keys!", + error: "Failed to create new API keys", + }, + ); + }; + + return ( + <> + setShowRegenerateModal(!showRegenerateModal)} + onAction={regenerate} + type={"danger"} + title={"Are you sure?"} + description={ + "Any applications that use your previously generated keys will stop working!" + } + /> + + + + + + } + > +
    { + void navigator.clipboard.writeText(activeProject.public); + toast.success("Copied your public API key"); + }} + > + +

    + {activeProject.public} +

    + +

    + Use this key for any front-end services. This key can only be used + to publish events. +

    +
    + +
    +
    { + void navigator.clipboard.writeText(activeProject.secret); + toast.success("Copied your secret API key"); + }} + > + +

    + {activeProject.secret} +

    + +

    + Use this key for any secure back-end services. This key gives + complete access to your Plunk setup. +

    +
    +
    +
    +
    + + ); +} diff --git a/packages/dashboard/src/pages/settings/identity.tsx b/packages/dashboard/src/pages/settings/identity.tsx new file mode 100644 index 0000000..76b9077 --- /dev/null +++ b/packages/dashboard/src/pages/settings/identity.tsx @@ -0,0 +1,356 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { IdentitySchemas, type UtilitySchemas } from "@plunk/shared"; +import { motion } from "framer-motion"; +import { Copy, Unlink } from "lucide-react"; +import React, { useEffect } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { + Alert, + Badge, + Card, + FullscreenLoader, + Input, + SettingTabs, + Table, +} from "../../components"; +import { Dashboard } from "../../layouts"; +import { AWS_REGION } from "../../lib/constants"; +import { + useActiveProject, + useActiveProjectVerifiedIdentity, + useProjects, +} from "../../lib/hooks/projects"; +import { network } from "../../lib/network"; + +interface EmailValues { + email: string; +} + +interface FromValues { + from: string; +} + +/** + * + */ +export default function Index() { + const activeProject = useActiveProject(); + const { mutate: projectsMutate } = useProjects(); + const { data: identity, mutate: identityMutate } = + useActiveProjectVerifiedIdentity(); + + const { + register, + handleSubmit, + formState: { errors }, + } = useForm({ + resolver: zodResolver(IdentitySchemas.create.omit({ id: true })), + }); + + const { + register: registerUpdate, + handleSubmit: handleSubmitUpdate, + formState: { errors: errorsUpdate }, + reset, + } = useForm({ + resolver: zodResolver(IdentitySchemas.update.omit({ id: true })), + }); + + useEffect(() => { + if (!activeProject) { + return; + } + + reset({ from: activeProject.from ?? undefined }); + }, [reset, activeProject]); + + if (!activeProject || !identity) { + return ; + } + + const create = async (data: EmailValues) => { + toast.promise( + network.fetch< + { + success: true; + tokens: string[]; + }, + typeof IdentitySchemas.create + >("POST", "/identities/create", { + id: activeProject.id, + ...data, + }), + { + loading: "Adding your domain", + success: (res) => { + void identityMutate({ tokens: res.tokens }, { revalidate: false }); + void projectsMutate(); + + return "Added your domain"; + }, + error: "Could not add domain", + }, + ); + }; + + const update = async (data: FromValues) => { + toast.promise( + network.fetch< + { + success: true; + }, + typeof IdentitySchemas.update + >("PUT", "/projects/update/identity", { + id: activeProject.id, + ...data, + }), + { + loading: "Updating your sender name", + success: "Updated your sender name", + error: "Could not update sender name", + }, + ); + + await identityMutate(); + await projectsMutate(); + }; + + const unlink = async () => { + toast.promise( + network.fetch< + { + success: true; + }, + typeof UtilitySchemas.id + >("POST", "/identities/reset", { + id: activeProject.id, + }), + { + loading: "Unlinking your domain", + success: "Unlinked your domain", + error: "Could not unlink domain", + }, + ); + + window.location.reload(); + }; + + return ( + <> + + + + + + + ) + } + > + {activeProject.email && !activeProject.verified ? ( + <> + + Please add the following records to{" "} + {activeProject.email.split("@")[1]} to verify{" "} + {activeProject.email}, this may take up to 15 minutes to + register.
    + In the meantime you can already start sending emails, we will + automatically switch to your domain once it is verified. +
    + +
    +
    TXT, + Key: ( +
    { + void navigator.clipboard.writeText("plunk"); + toast.success("Copied key to clipboard"); + }} + > +

    plunk

    + +
    + ), + Value: ( +
    { + void navigator.clipboard.writeText( + "v=spf1 include:amazonses.com ~all", + ); + toast.success("Copied value to clipboard"); + }} + > +

    + v=spf1 include:amazonses.com ~all +

    {" "} + +
    + ), + }, + { + type: MX, + Key: ( +
    { + void navigator.clipboard.writeText("plunk"); + toast.success("Copied key to clipboard"); + }} + > +

    plunk

    + +
    + ), + Value: ( +
    { + void navigator.clipboard.writeText( + `10 feedback-smtp.${AWS_REGION}.amazonses.com`, + ); + toast.success("Copied value to clipboard"); + }} + > +

    + 10 feedback-smtp.{AWS_REGION}.amazonses.com +

    + +
    + ), + }, + ...identity.tokens.map((token) => { + return { + Type: CNAME, + Key: ( +
    { + void navigator.clipboard.writeText( + `${token}._domainkey`, + ); + toast.success("Copied key to clipboard"); + }} + > +

    + {token}._domainkey +

    + +
    + ), + Value: ( +
    { + void navigator.clipboard.writeText( + `${token}.dkim.amazonses.com`, + ); + toast.success("Copied value to clipboard"); + }} + > +

    + {token}.dkim.amazonses.com +

    + +
    + ), + }; + }), + ]} + /> + + + ) : activeProject.email && activeProject.verified ? ( + <> + + You have confirmed {activeProject.email} as your domain. Any + emails sent by Plunk will now use this address. + + + ) : ( + <> + + + + + + + + Verify domain + + + + )} + + + +
    + + + + Save + + +
    + + + ); +} diff --git a/packages/dashboard/src/pages/settings/index.tsx b/packages/dashboard/src/pages/settings/index.tsx new file mode 100644 index 0000000..4543bff --- /dev/null +++ b/packages/dashboard/src/pages/settings/index.tsx @@ -0,0 +1,8 @@ +import { Redirect } from "../../components"; + +/** + * + */ +export default function Index() { + return ; +} diff --git a/packages/dashboard/src/pages/settings/members.tsx b/packages/dashboard/src/pages/settings/members.tsx new file mode 100644 index 0000000..7bb753f --- /dev/null +++ b/packages/dashboard/src/pages/settings/members.tsx @@ -0,0 +1,234 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { MembershipSchemas, type UtilitySchemas } from "@plunk/shared"; +import type { Project, Role } from "@prisma/client"; +import { motion } from "framer-motion"; +import { useRouter } from "next/router"; +import React, { useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { + Card, + FullscreenLoader, + Input, + Modal, + SettingTabs, + Table, +} from "../../components"; +import { Dashboard } from "../../layouts"; +import { + useActiveProject, + useActiveProjectMemberships, + useProjects, +} from "../../lib/hooks/projects"; +import { useUser } from "../../lib/hooks/users"; +import { network } from "../../lib/network"; + +interface EmailValues { + email: string; +} + +/** + * + */ +export default function Index() { + const [showInviteModal, setShowInviteModal] = useState(false); + const [showLeaveModal, setShowLeaveModal] = useState(false); + + const [project, setProject] = useState(); + + const router = useRouter(); + + const activeProject = useActiveProject(); + const { data: user } = useUser(); + const { data: projects, mutate: projectMutate } = useProjects(); + const { data: memberships, mutate: membershipMutate } = + useActiveProjectMemberships(); + + const { + register, + handleSubmit, + formState: { errors }, + } = useForm({ + resolver: zodResolver( + MembershipSchemas.invite.omit({ id: true, role: true }), + ), + }); + + if (activeProject && !project) { + setProject(activeProject); + } + + if (!project || !projects || !memberships || !user) { + return ; + } + + if (!activeProject) { + return ; + } + + const inviteAccount = (data: EmailValues) => { + toast.promise( + network.mock< + { + success: true; + members: { + userId: string; + email: string; + role: Role; + }[]; + }, + typeof MembershipSchemas.invite + >(project.secret, "POST", "/memberships/invite", { + id: project.id, + email: data.email, + role: "ADMIN", + }), + { + loading: "Adding new member", + success: async (result) => { + await membershipMutate(result.members); + setShowInviteModal(false); + + return "Added new member"; + }, + error: "Could not add new member!", + }, + ); + }; + + const kickAccount = (email: string) => { + void network + .fetch< + { + success: true; + members: { + userId: string; + email: string; + role: Role; + }[]; + }, + typeof MembershipSchemas.kick + >("POST", "/memberships/kick", { + id: project.id, + email, + }) + .then(async (res) => { + await membershipMutate(res.members); + }); + }; + + const leaveProject = () => { + void network + .fetch< + { + success: true; + memberships: Project[]; + }, + typeof UtilitySchemas.id + >("POST", "/memberships/leave", { + id: project.id, + }) + .then(async (res) => { + await projectMutate(res.memberships); + localStorage.removeItem("project"); + await router.push("/"); + window.location.reload(); + }); + }; + + return ( + <> + setShowLeaveModal(!showLeaveModal)} + onAction={leaveProject} + type={"danger"} + title={"Are you sure?"} + description={ + memberships.length === 1 + ? "You are the last person in this project, if you leave it we will automatically delete it!" + : "Leaving a project is permanent, you will lose access to the data and will need to be reinvited again." + } + /> + setShowInviteModal(!showInviteModal)} + onAction={handleSubmit(inviteAccount)} + type={"info"} + title={"Invite a new member"} + description={ + "Enter the email of the account you want to invite to this project. The person you want to invite needs to have an account on Plunk." + } + > + + + + + +
    { + return { + Account: membership.email, + Role: + membership.role.charAt(0).toUpperCase() + + membership.role.slice(1).toLowerCase(), + + Manage: + membership.userId === user.id ? ( + + ) : memberships.find( + (membership) => membership.userId === user.id, + )?.role === "OWNER" ? ( + + ) : ( + "" + ), + }; + })} + /> +
    +
    +

    + Invite team +

    +

    + By adding someone to your project you give them access to all + data present in your project including emails and your API key. +

    +
    + + setShowInviteModal(true)} + whileHover={{ scale: 1.05 }} + whileTap={{ scale: 0.9 }} + className={ + "ml-auto mt-4 self-end rounded bg-neutral-800 px-8 py-2.5 text-sm font-medium text-white" + } + > + Invite user + +
    + + + + ); +} diff --git a/packages/dashboard/src/pages/settings/project.tsx b/packages/dashboard/src/pages/settings/project.tsx new file mode 100644 index 0000000..2c17305 --- /dev/null +++ b/packages/dashboard/src/pages/settings/project.tsx @@ -0,0 +1,197 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { ProjectSchemas, type UtilitySchemas } from "@plunk/shared"; +import { network } from "dashboard/src/lib/network"; +import { motion } from "framer-motion"; +import { useRouter } from "next/router"; +import React, { useEffect, useState } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { + Card, + FullscreenLoader, + Input, + Modal, + SettingTabs, +} from "../../components"; +import { Dashboard } from "../../layouts"; +import { + useActiveProject, + useActiveProjectMemberships, + useProjects, +} from "../../lib/hooks/projects"; +import { useUser } from "../../lib/hooks/users"; + +interface ProjectValues { + name: string; + url: string; +} + +/** + * + */ +export default function Index() { + const router = useRouter(); + const activeProject = useActiveProject(); + const { data: user } = useUser(); + const { data: memberships } = useActiveProjectMemberships(); + const { mutate: projectsMutate } = useProjects(); + + const { + register, + handleSubmit, + formState: { errors }, + reset, + } = useForm({ + resolver: zodResolver(ProjectSchemas.update.omit({ id: true })), + }); + + const [showDeleteModal, setShowDeleteModal] = useState(false); + + useEffect(() => { + if (!activeProject) { + return; + } + + reset(activeProject); + }, [reset, activeProject]); + + if (!activeProject || !memberships || !user) { + return ; + } + + const update = async (data: ProjectValues) => { + toast.promise( + network.fetch< + { + success: true; + }, + typeof ProjectSchemas.update + >("PUT", "/projects/update/", { + id: activeProject.id, + ...data, + }), + { + loading: "Updating your project", + success: "Updated your project", + error: "Could not update your project", + }, + ); + + await projectsMutate(); + }; + + const deleteProject = async () => { + setShowDeleteModal(!showDeleteModal); + + await fetch("/api/plunk", { + method: "POST", + body: JSON.stringify({ + event: "project-deleted", + email: user.email, + data: { + project: activeProject.name, + }, + }), + headers: { "Content-Type": "application/json" }, + }); + + toast.promise( + network + .fetch< + { + success: true; + }, + typeof UtilitySchemas.id + >("DELETE", "/projects/delete", { + id: activeProject.id, + }) + .then(async () => { + localStorage.removeItem("project"); + await router.push("/"); + window.location.reload(); + }), + { + loading: "Deleting your project", + success: "Deleted your project", + error: "Could not delete your project", + }, + ); + }; + + return ( + <> + setShowDeleteModal(!showDeleteModal)} + onAction={deleteProject} + type={"danger"} + title={"Are you sure?"} + description={ + "All data associated with this project will also be permanently deleted. This action cannot be reversed!" + } + /> + + + +
    +
    + + +
    + + Save + + +
    + {memberships.find((membership) => membership.userId === user.id) + ?.role === "OWNER" ? ( + +
    +
    +

    + Delete your project +

    +

    + Deleting your project may have unwanted consequences. All data + associated with this project will get deleted and can not be + recovered!{" "} +

    +
    + +
    +
    + ) : null} +
    + + ); +} diff --git a/packages/dashboard/src/pages/subscribe/[id].tsx b/packages/dashboard/src/pages/subscribe/[id].tsx new file mode 100644 index 0000000..94d6df1 --- /dev/null +++ b/packages/dashboard/src/pages/subscribe/[id].tsx @@ -0,0 +1,179 @@ +import type { UtilitySchemas } from "@plunk/shared"; +import type { User } from "@prisma/client"; +import { motion } from "framer-motion"; +import { NextSeo } from "next-seo"; +import { useRouter } from "next/router"; +import React, { useState } from "react"; +import { toast } from "sonner"; +import { FullscreenLoader, Redirect } from "../../components"; +import { useContact } from "../../lib/hooks/contacts"; +import { network } from "../../lib/network"; + +/** + * + */ +export default function Index() { + const router = useRouter(); + + if (!router.isReady) { + return ; + } + + const { data: contact, error } = useContact({ + id: router.query.id as string, + withProject: true, + }); + const [submitted, setSubmitted] = useState< + "initial" | "loading" | "submitted" + >("initial"); + + if (error) { + return ; + } + + if (!contact) { + return ; + } + + const subscribe = async () => { + setSubmitted("loading"); + + toast.promise( + network.mock( + contact.project.public, + "POST", + "/v1/contacts/subscribe", + { + id: contact.id, + }, + ), + { + loading: "Subscribing...", + success: "Thank you for subscribing!", + error: "Could not subscribe you!", + }, + ); + + setSubmitted("submitted"); + }; + + return ( + <> + +
    +
    + {submitted === "submitted" ? ( + <> + + + + + + +

    + You have been subscribed! +

    + + ) : ( + <> +

    + Confirm your subscription? +

    +

    + By confirming your subscription to {contact.project.name} for{" "} + {contact.email} you agree to receive emails from us. +

    +
    + + {submitted === "loading" ? ( + + + + + ) : ( + "Subscribe" + )} + +
    + + )} +
    +
    + + ); +} diff --git a/packages/dashboard/src/pages/templates/[id].tsx b/packages/dashboard/src/pages/templates/[id].tsx new file mode 100644 index 0000000..cf64f7b --- /dev/null +++ b/packages/dashboard/src/pages/templates/[id].tsx @@ -0,0 +1,355 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { TemplateSchemas, type UtilitySchemas } from "@plunk/shared"; +import type { Template } from "@prisma/client"; +import { AnimatePresence, motion } from "framer-motion"; +import { Save } from "lucide-react"; +import { useRouter } from "next/router"; +import React, { useEffect } from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { + Card, + Dropdown, + Editor, + FullscreenLoader, + Input, + Tooltip, +} from "../../components"; +import { Dashboard } from "../../layouts"; +import { useActiveProject } from "../../lib/hooks/projects"; +import { useTemplate, useTemplates } from "../../lib/hooks/templates"; +import { network } from "../../lib/network"; + +interface TemplateValues { + subject: string; + body: string; + type: "MARKETING" | "TRANSACTIONAL"; + style: "PLUNK" | "HTML"; +} + +/** + * + */ +export default function Index() { + const router = useRouter(); + + if (!router.isReady) { + return ; + } + + const project = useActiveProject(); + const { mutate } = useTemplates(); + const { data: template } = useTemplate(router.query.id as string); + + const { + register, + handleSubmit, + formState: { errors }, + watch, + setValue, + reset, + } = useForm({ + resolver: zodResolver(TemplateSchemas.update), + defaultValues: { + body: undefined, + }, + }); + + useEffect(() => { + if (!template) { + return; + } + + reset(template); + }, [reset, template]); + + if ( + !project || + !template || + (watch("body") as string | undefined) === undefined + ) { + return ; + } + + const update = (data: TemplateValues) => { + toast.promise( + network.mock( + project.secret, + "PUT", + "/v1/templates", + { + id: template.id, + ...data, + }, + ), + { + loading: "Saving your template", + success: () => { + void mutate(); + + return "Saved your template"; + }, + error: "Could not save your template!", + }, + ); + }; + + const duplicate = async (e: { preventDefault: () => void }) => { + e.preventDefault(); + toast.promise( + network.mock( + project.secret, + "POST", + "/v1/templates/duplicate", + { + id: template.id, + }, + ), + { + loading: "Duplicating your template", + success: () => { + void mutate(); + return "Duplicated your template"; + }, + error: "Could not duplicate your template!", + }, + ); + + await router.push("/templates"); + }; + + const remove = async (e: { preventDefault: () => void }) => { + e.preventDefault(); + + if (template.actions.length > 0) { + return toast.error( + "You cannot delete a template that is linked to an action!", + ); + } + + toast.promise( + network.mock( + project.secret, + "DELETE", + "/v1/templates", + { + id: template.id, + }, + ), + { + loading: "Deleting your template", + success: () => { + void mutate(); + return "Deleted your template"; + }, + error: "Could not delete your template!", + }, + ); + + await router.push("/templates"); + }; + + return ( + <> + + + + + + } + > +
    + + +
    + + + setValue("type", t as "MARKETING" | "TRANSACTIONAL") + } + values={[ + { name: "Marketing", value: "MARKETING" }, + { name: "Transactional", value: "TRANSACTIONAL" }, + ]} + selectedValue={watch("type")} + /> + + {errors.type?.message && ( + + {errors.type.message} + + )} + +
    + +
    + { + setValue("style", type); + setValue("body", value); + }} + /> + + {errors.body?.message && ( + + {errors.body.message} + + )} + +
    + +
    + { + e.preventDefault(); + return router.push("/templates"); + }} + className={ + "flex w-full justify-center rounded border border-neutral-300 bg-white px-6 py-2 text-base font-medium text-neutral-700 focus:outline-none focus:ring-2 focus:ring-neutral-800 focus:ring-offset-2 sm:mt-0 sm:w-auto sm:text-sm" + } + > + Cancel + + + + Save + +
    + +
    +
    + + ); +} diff --git a/packages/dashboard/src/pages/templates/index.tsx b/packages/dashboard/src/pages/templates/index.tsx new file mode 100644 index 0000000..284d3e4 --- /dev/null +++ b/packages/dashboard/src/pages/templates/index.tsx @@ -0,0 +1,165 @@ +import dayjs from "dayjs"; +import { motion } from "framer-motion"; +import { LayoutTemplate, Plus } from "lucide-react"; +import Link from "next/link"; +import React from "react"; +import { Alert, Badge, Card, Empty, Skeleton } from "../../components"; +import { Dashboard } from "../../layouts"; +import { useTemplates } from "../../lib/hooks/templates"; + +/** + * + */ +export default function Index() { + const { data: templates } = useTemplates(); + + return ( + <> + + {templates?.length === 0 && ( + +
    +

    + Want us to help you get started? We can help you build your + first action in less than 5 minutes. +

    + + + Build an action + +
    +
    + )} + + + + + + New + + + + } + > + {templates ? ( + templates.length > 0 ? ( + <> +
    + {templates + .sort((a, b) => { + if (a.actions.length > 0 && b.actions.length === 0) { + return -1; + } + if (a.actions.length === 0 && b.actions.length > 0) { + return 1; + } + if (a.subject < b.subject) { + return -1; + } + if (a.subject > b.subject) { + return 1; + } + return 0; + }) + .map((t) => { + return ( + <> +
    +
    + + + +
    +
    +

    + {t.subject} +

    + {t.actions.length > 0 && ( + Active + )} +
    +

    + Last edited {dayjs().to(t.updatedAt)} +

    +
    +
    +
    +
    +
    + + + + + + + Edit + +
    +
    +
    +
    + + ); + })} +
    + + ) : ( + <> + + + ) + ) : ( + + )} +
    +
    + + ); +} diff --git a/packages/dashboard/src/pages/templates/new.tsx b/packages/dashboard/src/pages/templates/new.tsx new file mode 100644 index 0000000..2a93a30 --- /dev/null +++ b/packages/dashboard/src/pages/templates/new.tsx @@ -0,0 +1,245 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { TemplateSchemas } from "@plunk/shared"; +import type { Template } from "@prisma/client"; +import { AnimatePresence, motion } from "framer-motion"; +import { useRouter } from "next/router"; +import React from "react"; +import { useForm } from "react-hook-form"; +import { toast } from "sonner"; +import { + Card, + Dropdown, + Editor, + FullscreenLoader, + Input, + Tooltip, +} from "../../components"; +import { Dashboard } from "../../layouts"; +import { useActiveProject } from "../../lib/hooks/projects"; +import { useTemplates } from "../../lib/hooks/templates"; +import { network } from "../../lib/network"; + +interface TemplateValues { + subject: string; + body: string; + type: "MARKETING" | "TRANSACTIONAL"; + style: "PLUNK" | "HTML"; +} + +const templates = { + blank: { + subject: "", + body: "", + }, +}; + +/** + * + */ +export default function Index() { + const router = useRouter(); + + const project = useActiveProject(); + const { mutate } = useTemplates(); + + const { + register, + handleSubmit, + formState: { errors }, + watch, + setValue, + } = useForm({ + resolver: zodResolver(TemplateSchemas.create), + defaultValues: { + ...templates.blank, + type: "MARKETING", + style: "PLUNK", + }, + }); + + if (!project) { + return ; + } + + const create = async (data: TemplateValues) => { + toast.promise( + network.mock( + project.secret, + "POST", + "/v1/templates", + { + ...data, + }, + ), + { + loading: "Creating new template", + success: () => { + void mutate(); + + return "Created new template!"; + }, + error: "Could not create new template!", + }, + ); + + await router.push("/templates"); + }; + + return ( + <> + + +
    + + +
    + + + setValue("type", t as "MARKETING" | "TRANSACTIONAL") + } + values={[ + { name: "Marketing", value: "MARKETING" }, + { name: "Transactional", value: "TRANSACTIONAL" }, + ]} + selectedValue={watch("type")} + /> + + {errors.type?.message && ( + + {errors.type.message} + + )} + +
    + +
    + { + setValue("body", value); + setValue("style", type); + }} + /> + + {errors.body?.message && ( + + {errors.body.message} + + )} + +
    + +
    + { + e.preventDefault(); + return router.push("/templates"); + }} + className={ + "flex w-full justify-center rounded border border-neutral-300 bg-white px-6 py-2 text-base font-medium text-neutral-700 focus:outline-none focus:ring-2 focus:ring-neutral-800 focus:ring-offset-2 sm:mt-0 sm:w-auto sm:text-sm" + } + > + Cancel + + + + + + + + Create + +
    + +
    +
    + + ); +} diff --git a/packages/dashboard/src/pages/unsubscribe/[id].tsx b/packages/dashboard/src/pages/unsubscribe/[id].tsx new file mode 100644 index 0000000..bcd1358 --- /dev/null +++ b/packages/dashboard/src/pages/unsubscribe/[id].tsx @@ -0,0 +1,180 @@ +import type { UtilitySchemas } from "@plunk/shared"; +import type { User } from "@prisma/client"; +import { motion } from "framer-motion"; +import { NextSeo } from "next-seo"; +import { useRouter } from "next/router"; +import React, { useState } from "react"; +import { toast } from "sonner"; +import { FullscreenLoader, Redirect } from "../../components"; +import { useContact } from "../../lib/hooks/contacts"; +import { network } from "../../lib/network"; + +/** + * + */ +export default function Index() { + const router = useRouter(); + + if (!router.isReady) { + return ; + } + + const { data: contact, error } = useContact({ + id: router.query.id as string, + withProject: true, + }); + const [submitted, setSubmitted] = useState< + "initial" | "loading" | "submitted" + >("initial"); + + if (error) { + return ; + } + + if (!contact) { + return ; + } + + const unsubscribe = () => { + setSubmitted("loading"); + + toast.promise( + network.mock( + contact.project.public, + "POST", + "/v1/contacts/unsubscribe", + { + id: contact.id, + }, + ), + { + loading: "Unsubscribing", + success: "Unsubscribed", + error: "Could not unsubscribe you!", + }, + ); + + setSubmitted("submitted"); + }; + + return ( + <> + +
    +
    + {submitted === "submitted" ? ( + <> + + + + + + +

    + You have been unsubscribed! +

    + + ) : ( + <> +

    + Are you sure you want to unsubscribe? +

    +

    + You will no longer receive emails from {contact.project.name} on + the email {contact.email} when you confirm that you want to + unsubscribe. +

    +
    + + {submitted === "loading" ? ( + + + + + ) : ( + "Unsubscribe" + )} + +
    + + )} +
    +
    + + ); +} diff --git a/packages/dashboard/styles/index.css b/packages/dashboard/styles/index.css new file mode 100644 index 0000000..c87c7f0 --- /dev/null +++ b/packages/dashboard/styles/index.css @@ -0,0 +1,58 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +html { + scroll-behavior: smooth; + -webkit-overflow-scrolling: touch; +} + +#nprogress .bar { + background: #171717 !important; +} + +.ProseMirror p.is-editor-empty:first-child::before { + @apply text-neutral-400; + content: attr(data-placeholder); + float: left; + height: 0; + pointer-events: none; +} + +.ProseMirror progress { + @apply rounded-xl w-full h-8 +} + +.ProseMirror progress::-webkit-progress-bar { + @apply bg-blue-100 rounded-xl +} + +.ProseMirror progress::-webkit-progress-value { + @apply bg-blue-500 rounded-xl +} + +.tippy-box[data-theme~="custom"] { + @apply border border-neutral-300 bg-neutral-50 text-neutral-800 shadow-xl +} + +circle { + animation: moveCircle 10s linear infinite; +} + +@keyframes moveCircle { + from { + stroke-dashoffset: 100%; + } + + to { + stroke-dashoffset: 0%; + } +} + +.revert-tailwind { + all: initial; +} + +.revert-tailwind > * { + all: revert; +} diff --git a/packages/dashboard/tailwind.config.js b/packages/dashboard/tailwind.config.js new file mode 100644 index 0000000..b0813e0 --- /dev/null +++ b/packages/dashboard/tailwind.config.js @@ -0,0 +1,17 @@ +const defaultTheme = require('tailwindcss/defaultTheme'); + +module.exports = { + content: ['./src/**/*.{ts,tsx}'], + theme: { + fontFamily: { + sans: ['"Inter Var"', 'Inter', ...defaultTheme.fontFamily.sans], + mono: ['"Jetbrains Mono"', ...defaultTheme.fontFamily.mono], + }, + }, + plugins: [ + require('@tailwindcss/forms'), + require('@tailwindcss/aspect-ratio'), + require('@tailwindcss/typography'), + require('tailwind-scrollbar')({nocompatible: true}), + ], +}; diff --git a/packages/dashboard/tsconfig.json b/packages/dashboard/tsconfig.json new file mode 100644 index 0000000..4d416f3 --- /dev/null +++ b/packages/dashboard/tsconfig.json @@ -0,0 +1,24 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], + "noEmit": true, + "jsx": "preserve", + "incremental": true + }, + "exclude": [ + "node_modules", + "dist", + "build", + ".next" + ], + "include": [ + "src", + "next-env.d.ts", + "custom.d.ts" + ] +} diff --git a/packages/shared/package.json b/packages/shared/package.json new file mode 100644 index 0000000..b0418d0 --- /dev/null +++ b/packages/shared/package.json @@ -0,0 +1,20 @@ +{ + "name": "@plunk/shared", + "version": "1.0.0", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "private": true, + "dependencies": { + "dayjs": "^1.11.12", + "zod": "^3.23.8" + }, + "devDependencies": { + "typescript": "^5.5.3" + }, + "scripts": { + "watch": "tsc && tsc -w", + "build": "tsc", + "dev": "yarn watch", + "clean": "rimraf node_modules dist .turbo" + } +} diff --git a/packages/shared/src/index.ts b/packages/shared/src/index.ts new file mode 100644 index 0000000..92e6d0c --- /dev/null +++ b/packages/shared/src/index.ts @@ -0,0 +1,264 @@ +import {z} from 'zod'; +import {TemplateStyle, TemplateType} from '@prisma/client'; + +const email = z + .string({invalid_type_error: 'Email needs to be a string', required_error: 'Email is required'}) + .email({message: 'Invalid email address'}) + .transform(e => e.toLowerCase()); + +const password = z.string().min(6, 'Password needs to be at least 6 characters long'); + +const id = z + .string({invalid_type_error: 'ID needs to be a string', required_error: 'ID is required'}) + .uuid({message: 'Id needs to be a valid UUID'}); + +export const UtilitySchemas = { + id: z.object({ + id, + }), + email: z.object({ + email, + }), + pagination: z.object({ + page: z + .number({ + invalid_type_error: 'Page needs to be a number', + required_error: 'Page is required', + }) + .min(1, 'Page needs to be at least 1') + .default(1) + .or( + z.string().transform(s => { + return Number(s); + }), + ), + }), +}; + +export const UserSchemas = { + credentials: z.object({ + email: email, + password: password, + }), +}; + +const zodSchema = z.record( + z.union( + [ + z + .string({ + invalid_type_error: + 'Metadata can only be a string, array of strings or a non-persistent object (https://docs.useplunk.com/working-with-contacts/metadata#non-persistent-metadata)', + }) + .transform(s => { + return {persistent: true, value: s}; + }), + z + .array( + z.string({ + invalid_type_error: + 'Metadata can only be a string, array of strings or a non-persistent object (https://docs.useplunk.com/working-with-contacts/metadata#non-persistent-metadata)', + }), + ) + .transform(s => { + return {persistent: true, value: s}; + }), + z.object( + { + persistent: z.boolean({invalid_type_error: 'Persistent should be a boolean'}).optional().default(true), + value: z.union([z.string(), z.array(z.string())], { + invalid_type_error: + 'Metadata can only be a string, array of strings or a non-persistent object (https://docs.useplunk.com/working-with-contacts/metadata#non-persistent-metadata)', + }), + }, + { + invalid_type_error: + 'Metadata can only be a string, array of strings or a non-persistent object (https://docs.useplunk.com/working-with-contacts/metadata#non-persistent-metadata)', + }, + ), + ], + { + invalid_type_error: + 'Metadata can only be a string, array of strings or a non-persistent object (https://docs.useplunk.com/working-with-contacts/metadata#non-persistent-metadata)', + }, + ), + {invalid_type_error: 'Metadata should be an object (https://docs.useplunk.com/working-with-contacts/metadata)'}, +); +export const EventSchemas = { + post: z.object({ + email, + subscribed: z + .boolean({ + invalid_type_error: + 'Subscribed should be a boolean. Read more: https://docs.useplunk.com/api-reference/actions/track', + }) + .nullish(), + event: z + .string({ + required_error: 'Event is required. Read more: https://docs.useplunk.com/api-reference/actions/track', + invalid_type_error: + 'Event can only be a string. Read more: https://docs.useplunk.com/api-reference/actions/track', + }) + .transform(n => n.toLowerCase()) + .transform(n => n.replace(/ /g, '-')), + data: zodSchema.nullish(), + }), + send: z.object({ + subscribed: z.boolean({invalid_type_error: 'Subscribed should be a boolean'}).nullish(), + from: email.nullish(), + name: z.string().nullish(), + reply: email.nullish(), + to: z + .array(email) + .max(5, 'You can only send transactional emails to 5 people at a time') + .or(email.transform(e => [e])), + subject: z.string({ + required_error: 'Subject is required. Read more: https://docs.useplunk.com/api-reference/transactional/send', + }), + body: z.string({ + required_error: 'Body is required. Read more: https://docs.useplunk.com/api-reference/transactional/send', + }), + headers: z.record(z.string()).nullish(), + }), +}; + +export const CampaignSchemas = { + send: z.object({ + id, + live: z.boolean().default(false), + delay: z.number().int('Delay needs to be a whole number').nonnegative('Delay needs to be a positive number'), + }), + create: z.object({ + subject: z + .string() + .min(1, 'Subject needs to be at least 1 character long') + .max(70, 'Subject needs to be less than 70 characters long'), + body: z.string().min(1, 'Body needs to be at least 1 character long'), + recipients: z.array(z.string()), + style: z.nativeEnum(TemplateStyle).default('PLUNK'), + }), + update: z.object({ + id, + subject: z + .string() + .min(1, 'Subject needs to be at least 1 character long') + .max(70, 'Subject needs to be less than 70 characters long'), + body: z.string().min(1, 'Body needs to be at least 1 character long'), + recipients: z.array(z.string()), + style: z.nativeEnum(TemplateStyle).default('PLUNK'), + }), +}; + +export const ActionSchemas = { + create: z.object({ + name: z.string().min(1, 'Name needs to be at least 1 character long'), + runOnce: z.boolean().default(false), + delay: z.number().int('Delay needs to be a whole number').nonnegative('Delay needs to be a positive number'), + template: id, + events: z.array(id).min(1, 'Select at least one event'), + notevents: z.array(id).optional().default([]), + }), + update: z.object({ + id, + name: z.string().min(1, 'Name needs to be at least 1 character long'), + runOnce: z.boolean().default(false), + delay: z.number().int('Delay needs to be a whole number').nonnegative('Delay needs to be a positive number'), + template: id, + events: z.array(id).default([]), + notevents: z.array(id).optional().default([]), + }), +}; + +export const ContactSchemas = { + create: z.object({ + email, + data: z + .object({}) + .catchall(z.union([z.string(), z.array(z.string())])) + .or(z.string().transform(s => (s === '' ? null : JSON.parse(s)))) + .nullish(), + subscribed: z.boolean(), + }), + update: z.object({ + id, + email, + data: z + .object({}) + .catchall(z.union([z.string(), z.array(z.string())])) + .or(z.string().transform(s => (s === '' ? null : JSON.parse(s)))) + .nullish(), + subscribed: z.boolean(), + }), +}; + +export const TemplateSchemas = { + create: z.object({ + subject: z.string().min(1, "Subject can't be empty").max(70, 'Subject needs to be less than 70 characters long'), + body: z.string().min(1, "Body can't be empty"), + type: z.nativeEnum(TemplateType).default('MARKETING'), + style: z.nativeEnum(TemplateStyle).default('PLUNK'), + }), + update: z.object({ + id, + subject: z.string().min(1, "Subject can't be empty").max(70, 'Subject needs to be less than 70 characters long'), + body: z.string().min(1, "Body can't be empty"), + type: z.nativeEnum(TemplateType).default('MARKETING'), + style: z.nativeEnum(TemplateStyle).default('PLUNK'), + }), +}; + +export const MembershipSchemas = { + invite: z.object({ + id, + email, + role: z.enum(['MEMBER', 'ADMIN']).default('MEMBER'), + }), + kick: z.object({ + id, + email, + }), +}; + +export const ProjectSchemas = { + secret: z.object({ + secret: z.string(), + }), + create: z.object({ + name: z.string().min(1, "Name can't be empty"), + + url: z + .string() + .regex(/^(?:(?:https?):\/\/)?(?:[\w-]+\.)+[a-z]{2,}(?:\/[^\s]*)?$/) + .transform(u => (u.startsWith('http') ? u : `https://${u}`)), + }), + update: z.object({ + id: id, + name: z.string().min(1, "Name can't be empty"), + + url: z + .string() + .regex(/^(?:(?:https?):\/\/)?(?:[\w-]+\.)+[a-z]{2,}(?:\/[^\s]*)?$/) + .transform(u => (u.startsWith('http') ? u : `https://${u}`)), + }), + analytics: z.object({ + method: z.enum(['week', 'month', 'year']).default('week'), + }), +}; + +export const IdentitySchemas = { + create: z.object({ + id: id, + email: email.refine( + e => { + return !['gmail.com', 'outlook.com', 'hotmail.com', 'yahoo.com', 'useplunk.com', 'useplunk.dev'].includes( + e.split('@')[1], + ); + }, + {message: 'Please use your own domain'}, + ), + }), + update: z.object({ + id: id, + from: z.string().min(1, "Name can't be empty"), + }), +}; diff --git a/packages/shared/tsconfig.json b/packages/shared/tsconfig.json new file mode 100644 index 0000000..531f16b --- /dev/null +++ b/packages/shared/tsconfig.json @@ -0,0 +1,13 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "dist", + "declaration": true, + "target": "ES2020", + "lib": ["ES2020", "DOM"], + "esModuleInterop": true, + "moduleResolution": "node" + }, + "exclude": ["dist", "node_modules"] +} diff --git a/prisma/.env.example b/prisma/.env.example new file mode 100644 index 0000000..335dc4e --- /dev/null +++ b/prisma/.env.example @@ -0,0 +1 @@ +DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres \ No newline at end of file diff --git a/prisma/migrations/20240719085125_init/migration.sql b/prisma/migrations/20240719085125_init/migration.sql new file mode 100644 index 0000000..6024150 --- /dev/null +++ b/prisma/migrations/20240719085125_init/migration.sql @@ -0,0 +1,330 @@ +-- CreateEnum +CREATE TYPE "TemplateType" AS ENUM ('MARKETING', 'TRANSACTIONAL'); + +-- CreateEnum +CREATE TYPE "TemplateStyle" AS ENUM ('PLUNK', 'HTML'); + +-- CreateEnum +CREATE TYPE "EmailStatus" AS ENUM ('SENT', 'DELIVERED', 'BOUNCED', 'OPENED', 'COMPLAINT'); + +-- CreateEnum +CREATE TYPE "CampaignStatus" AS ENUM ('DRAFT', 'SCHEDULED', 'DELIVERED'); + +-- CreateEnum +CREATE TYPE "Role" AS ENUM ('OWNER', 'ADMIN', 'MEMBER'); + +-- CreateTable +CREATE TABLE "users" ( + "id" TEXT NOT NULL, + "email" TEXT NOT NULL, + "password" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "users_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "projects" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "url" TEXT NOT NULL, + "verified" BOOLEAN NOT NULL DEFAULT false, + "email" TEXT, + "from" TEXT, + "public" TEXT NOT NULL, + "secret" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "projects_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "projectmemberhips" ( + "role" "Role" NOT NULL DEFAULT 'MEMBER', + "userId" TEXT NOT NULL, + "projectId" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "projectmemberhips_pkey" PRIMARY KEY ("userId","projectId") +); + +-- CreateTable +CREATE TABLE "events" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "projectId" TEXT NOT NULL, + "templateId" TEXT, + "campaignId" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "events_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "templates" ( + "id" TEXT NOT NULL, + "subject" TEXT NOT NULL, + "body" TEXT NOT NULL, + "type" "TemplateType" NOT NULL, + "style" "TemplateStyle" NOT NULL, + "projectId" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "templates_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "actions" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "runOnce" BOOLEAN NOT NULL DEFAULT false, + "delay" INTEGER NOT NULL DEFAULT 0, + "projectId" TEXT NOT NULL, + "templateId" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "actions_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "campaigns" ( + "id" TEXT NOT NULL, + "subject" TEXT NOT NULL, + "body" TEXT NOT NULL, + "status" "CampaignStatus" NOT NULL DEFAULT 'DRAFT', + "delivered" TIMESTAMP(3), + "style" "TemplateStyle" NOT NULL, + "projectId" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "campaigns_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "triggers" ( + "id" TEXT NOT NULL, + "contactId" TEXT NOT NULL, + "eventId" TEXT, + "actionId" TEXT, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "triggers_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "contacts" ( + "id" TEXT NOT NULL, + "email" TEXT NOT NULL, + "subscribed" BOOLEAN NOT NULL DEFAULT true, + "data" TEXT, + "projectId" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "contacts_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "emails" ( + "id" TEXT NOT NULL, + "messageId" TEXT NOT NULL, + "subject" TEXT, + "body" TEXT, + "status" "EmailStatus" NOT NULL DEFAULT 'SENT', + "projectId" TEXT, + "actionId" TEXT, + "campaignId" TEXT, + "contactId" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "emails_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "clicks" ( + "id" TEXT NOT NULL, + "link" TEXT NOT NULL, + "emailId" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "clicks_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "tasks" ( + "id" TEXT NOT NULL, + "runBy" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "actionId" TEXT, + "campaignId" TEXT, + "contactId" TEXT NOT NULL, + "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "tasks_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "_ActionToEvent" ( + "A" TEXT NOT NULL, + "B" TEXT NOT NULL +); + +-- CreateTable +CREATE TABLE "_ActionToNotEvent" ( + "A" TEXT NOT NULL, + "B" TEXT NOT NULL +); + +-- CreateTable +CREATE TABLE "_CampaignToContact" ( + "A" TEXT NOT NULL, + "B" TEXT NOT NULL +); + +-- CreateIndex +CREATE UNIQUE INDEX "users_email_key" ON "users"("email"); + +-- CreateIndex +CREATE UNIQUE INDEX "projects_public_key" ON "projects"("public"); + +-- CreateIndex +CREATE UNIQUE INDEX "projects_secret_key" ON "projects"("secret"); + +-- CreateIndex +CREATE INDEX "events_projectId_templateId_campaignId_idx" ON "events"("projectId", "templateId", "campaignId"); + +-- CreateIndex +CREATE INDEX "templates_projectId_idx" ON "templates"("projectId"); + +-- CreateIndex +CREATE INDEX "actions_projectId_templateId_idx" ON "actions"("projectId", "templateId"); + +-- CreateIndex +CREATE INDEX "campaigns_projectId_idx" ON "campaigns"("projectId"); + +-- CreateIndex +CREATE INDEX "triggers_contactId_eventId_actionId_createdAt_idx" ON "triggers"("contactId", "eventId", "actionId", "createdAt"); + +-- CreateIndex +CREATE INDEX "contacts_projectId_idx" ON "contacts"("projectId"); + +-- CreateIndex +CREATE UNIQUE INDEX "emails_messageId_key" ON "emails"("messageId"); + +-- CreateIndex +CREATE INDEX "emails_projectId_actionId_campaignId_contactId_createdAt_idx" ON "emails"("projectId", "actionId", "campaignId", "contactId", "createdAt"); + +-- CreateIndex +CREATE INDEX "clicks_emailId_createdAt_idx" ON "clicks"("emailId", "createdAt"); + +-- CreateIndex +CREATE INDEX "tasks_runBy_createdAt_idx" ON "tasks"("runBy", "createdAt"); + +-- CreateIndex +CREATE UNIQUE INDEX "_ActionToEvent_AB_unique" ON "_ActionToEvent"("A", "B"); + +-- CreateIndex +CREATE INDEX "_ActionToEvent_B_index" ON "_ActionToEvent"("B"); + +-- CreateIndex +CREATE UNIQUE INDEX "_ActionToNotEvent_AB_unique" ON "_ActionToNotEvent"("A", "B"); + +-- CreateIndex +CREATE INDEX "_ActionToNotEvent_B_index" ON "_ActionToNotEvent"("B"); + +-- CreateIndex +CREATE UNIQUE INDEX "_CampaignToContact_AB_unique" ON "_CampaignToContact"("A", "B"); + +-- CreateIndex +CREATE INDEX "_CampaignToContact_B_index" ON "_CampaignToContact"("B"); + +-- AddForeignKey +ALTER TABLE "projectmemberhips" ADD CONSTRAINT "projectmemberhips_userId_fkey" FOREIGN KEY ("userId") REFERENCES "users"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "projectmemberhips" ADD CONSTRAINT "projectmemberhips_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "projects"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "events" ADD CONSTRAINT "events_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "projects"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "events" ADD CONSTRAINT "events_templateId_fkey" FOREIGN KEY ("templateId") REFERENCES "templates"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "events" ADD CONSTRAINT "events_campaignId_fkey" FOREIGN KEY ("campaignId") REFERENCES "campaigns"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "templates" ADD CONSTRAINT "templates_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "projects"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "actions" ADD CONSTRAINT "actions_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "projects"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "actions" ADD CONSTRAINT "actions_templateId_fkey" FOREIGN KEY ("templateId") REFERENCES "templates"("id") ON DELETE RESTRICT ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "campaigns" ADD CONSTRAINT "campaigns_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "projects"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "triggers" ADD CONSTRAINT "triggers_contactId_fkey" FOREIGN KEY ("contactId") REFERENCES "contacts"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "triggers" ADD CONSTRAINT "triggers_eventId_fkey" FOREIGN KEY ("eventId") REFERENCES "events"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "triggers" ADD CONSTRAINT "triggers_actionId_fkey" FOREIGN KEY ("actionId") REFERENCES "actions"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "contacts" ADD CONSTRAINT "contacts_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "projects"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "emails" ADD CONSTRAINT "emails_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "projects"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "emails" ADD CONSTRAINT "emails_actionId_fkey" FOREIGN KEY ("actionId") REFERENCES "actions"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "emails" ADD CONSTRAINT "emails_campaignId_fkey" FOREIGN KEY ("campaignId") REFERENCES "campaigns"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "emails" ADD CONSTRAINT "emails_contactId_fkey" FOREIGN KEY ("contactId") REFERENCES "contacts"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "clicks" ADD CONSTRAINT "clicks_emailId_fkey" FOREIGN KEY ("emailId") REFERENCES "emails"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "tasks" ADD CONSTRAINT "tasks_actionId_fkey" FOREIGN KEY ("actionId") REFERENCES "actions"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "tasks" ADD CONSTRAINT "tasks_campaignId_fkey" FOREIGN KEY ("campaignId") REFERENCES "campaigns"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "tasks" ADD CONSTRAINT "tasks_contactId_fkey" FOREIGN KEY ("contactId") REFERENCES "contacts"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_ActionToEvent" ADD CONSTRAINT "_ActionToEvent_A_fkey" FOREIGN KEY ("A") REFERENCES "actions"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_ActionToEvent" ADD CONSTRAINT "_ActionToEvent_B_fkey" FOREIGN KEY ("B") REFERENCES "events"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_ActionToNotEvent" ADD CONSTRAINT "_ActionToNotEvent_A_fkey" FOREIGN KEY ("A") REFERENCES "actions"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_ActionToNotEvent" ADD CONSTRAINT "_ActionToNotEvent_B_fkey" FOREIGN KEY ("B") REFERENCES "events"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_CampaignToContact" ADD CONSTRAINT "_CampaignToContact_A_fkey" FOREIGN KEY ("A") REFERENCES "campaigns"("id") ON DELETE CASCADE ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "_CampaignToContact" ADD CONSTRAINT "_CampaignToContact_B_fkey" FOREIGN KEY ("B") REFERENCES "contacts"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 0000000..fbffa92 --- /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..65ee874 --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,329 @@ +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model User { + id String @id @default(uuid()) + + // Credentials + email String @unique + password String? + + // Relations + memberships ProjectMembership[] + + // Timestamps + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@map("users") +} + +model Project { + id String @id @default(uuid()) + + // Details + name String + url String + + // Verified domain + verified Boolean @default(false) + email String? + from String? + + // API access + public String @unique + secret String @unique + + // Relations + memberships ProjectMembership[] + contacts Contact[] + campaigns Campaign[] + actions Action[] + events Event[] + templates Template[] + emails Email[] + + // Timestamps + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@map("projects") +} + +// Table to maintain many-to-many relationship between Account and Project +model ProjectMembership { + role Role @default(MEMBER) + + // Relations + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + userId String + project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) + projectId String + + // Timestamps + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@id(fields: [userId, projectId]) + @@map("projectmemberhips") +} + +model Event { + id String @id @default(uuid()) + + // Details + name String + + // Relations + project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) + projectId String + + template Template? @relation(fields: [templateId], references: [id], onDelete: Cascade) + templateId String? + + campaign Campaign? @relation(fields: [campaignId], references: [id], onDelete: Cascade) + campaignId String? + + actions Action[] @relation("ActionToEvent") + notActions Action[] @relation("ActionToNotEvent") + triggers Trigger[] + + // Timestamps + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index(fields: [projectId, templateId, campaignId]) + @@map("events") +} + +model Template { + id String @id @default(uuid()) + + // Details + subject String + body String + + type TemplateType + style TemplateStyle + + // Relations + project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) + projectId String + events Event[] + actions Action[] + + // Timestamps + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index(fields: [projectId]) + @@map("templates") +} + +model Action { + id String @id @default(uuid()) + + // Details + name String + runOnce Boolean @default(false) + delay Int @default(0) + + // Relations + project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) + projectId String + + template Template @relation(fields: [templateId], references: [id]) + templateId String + + events Event[] @relation("ActionToEvent") + notevents Event[] @relation("ActionToNotEvent") + + triggers Trigger[] + emails Email[] + tasks Task[] + + // Timestamps + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index(fields: [projectId, templateId]) + @@map("actions") +} + +model Campaign { + id String @id @default(uuid()) + + subject String + body String + status CampaignStatus @default(DRAFT) + delivered DateTime? + + style TemplateStyle + // Relations + project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) + projectId String + emails Email[] + tasks Task[] + recipients Contact[] + events Event[] + + // Timestamps + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index(fields: [projectId]) + @@map("campaigns") +} + +model Trigger { + id String @id @default(uuid()) + + // Relations + contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade) + contactId String + + event Event? @relation(fields: [eventId], references: [id], onDelete: Cascade) + eventId String? + action Action? @relation(fields: [actionId], references: [id], onDelete: Cascade) + actionId String? + + // Timestamps + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index(fields: [contactId, eventId, actionId, createdAt]) + @@map("triggers") +} + +model Contact { + id String @id @default(uuid()) + + // Details + email String + subscribed Boolean @default(true) + data String? + + // Relations + project Project @relation(fields: [projectId], references: [id], onDelete: Cascade) + projectId String + + emails Email[] + triggers Trigger[] + tasks Task[] + campaigns Campaign[] + + // Timestamps + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index(fields: [projectId]) + @@map("contacts") +} + +model Email { + id String @id @default(uuid()) + + messageId String @unique + subject String? + body String? + status EmailStatus @default(SENT) + + // Relations + project Project? @relation(fields: [projectId], references: [id], onDelete: Cascade) + projectId String? + + action Action? @relation(fields: [actionId], references: [id], onDelete: Cascade) + actionId String? + + campaign Campaign? @relation(fields: [campaignId], references: [id], onDelete: Cascade) + campaignId String? + + contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade) + contactId String + + clicks Click[] + + // Timestamps + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index(fields: [projectId, actionId, campaignId, contactId, createdAt]) + @@map("emails") +} + +model Click { + id String @id @default(uuid()) + + link String + + // Relations + email Email @relation(fields: [emailId], references: [id], onDelete: Cascade) + emailId String + + // Timestamps + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([emailId, createdAt]) + @@map("clicks") +} + +model Task { + id String @id @default(uuid()) + + // Details + runBy DateTime @default(now()) + + // Relations + action Action? @relation(fields: [actionId], references: [id], onDelete: Cascade) + actionId String? + + campaign Campaign? @relation(fields: [campaignId], references: [id], onDelete: Cascade) + campaignId String? + + contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade) + contactId String + + // Timestamps + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([runBy, createdAt]) + @@map("tasks") +} + +enum TemplateType { + MARKETING + TRANSACTIONAL +} + +enum TemplateStyle { + PLUNK + HTML +} + +enum EmailStatus { + SENT + DELIVERED + BOUNCED + OPENED + COMPLAINT +} + +enum CampaignStatus { + DRAFT + SCHEDULED + DELIVERED +} + +enum Role { + OWNER + ADMIN + MEMBER +} diff --git a/tools/preinstall.js b/tools/preinstall.js new file mode 100644 index 0000000..30365be --- /dev/null +++ b/tools/preinstall.js @@ -0,0 +1,10 @@ +/** + * Forces use of yarn over npm + * + * @description Do NOT allow using `npm` as package manager. + */ +if (!process.env.npm_execpath.includes('yarn')) { + console.error('You must use Yarn to install dependencies:'); + console.error('$ yarn install\n'); + process.exit(1); +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..160a9c6 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,32 @@ +{ + "compilerOptions": { + "target": "es2020", + "allowJs": false, + "skipLibCheck": true, + "esModuleInterop": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noFallthroughCasesInSwitch": true, + "module": "CommonJS", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "downlevelIteration": true, + "emitDecoratorMetadata": true, + "declaration": true, + "experimentalDecorators": true, + "baseUrl": "packages", + "paths": { + "@plunk/shared": [ + "./packages/shared/" + ], + "@plunk/shared/*": [ + "./packages/shared/*" + ] + } + }, + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..5f3a3cb --- /dev/null +++ b/yarn.lock @@ -0,0 +1,13764 @@ +# This file is generated by running "yarn install" inside your project. +# Manual changes might be lost - proceed with caution! + +__metadata: + version: 8 + cacheKey: 10c0 + +"@alloc/quick-lru@npm:^5.2.0": + version: 5.2.0 + resolution: "@alloc/quick-lru@npm:5.2.0" + checksum: 10c0/7b878c48b9d25277d0e1a9b8b2f2312a314af806b4129dc902f2bc29ab09b58236e53964689feec187b28c80d2203aff03829754773a707a8a5987f1b7682d92 + languageName: node + linkType: hard + +"@ampproject/remapping@npm:^2.2.0": + version: 2.3.0 + resolution: "@ampproject/remapping@npm:2.3.0" + dependencies: + "@jridgewell/gen-mapping": "npm:^0.3.5" + "@jridgewell/trace-mapping": "npm:^0.3.24" + checksum: 10c0/81d63cca5443e0f0c72ae18b544cc28c7c0ec2cea46e7cb888bb0e0f411a1191d0d6b7af798d54e30777d8d1488b2ec0732aac2be342d3d7d3ffd271c6f489ed + languageName: node + linkType: hard + +"@aws-crypto/crc32@npm:5.2.0": + version: 5.2.0 + resolution: "@aws-crypto/crc32@npm:5.2.0" + dependencies: + "@aws-crypto/util": "npm:^5.2.0" + "@aws-sdk/types": "npm:^3.222.0" + tslib: "npm:^2.6.2" + checksum: 10c0/eab9581d3363af5ea498ae0e72de792f54d8890360e14a9d8261b7b5c55ebe080279fb2556e07994d785341cdaa99ab0b1ccf137832b53b5904cd6928f2b094b + languageName: node + linkType: hard + +"@aws-crypto/crc32c@npm:5.2.0": + version: 5.2.0 + resolution: "@aws-crypto/crc32c@npm:5.2.0" + dependencies: + "@aws-crypto/util": "npm:^5.2.0" + "@aws-sdk/types": "npm:^3.222.0" + tslib: "npm:^2.6.2" + checksum: 10c0/223efac396cdebaf5645568fa9a38cd0c322c960ae1f4276bedfe2e1031d0112e49d7d39225d386354680ecefae29f39af469a84b2ddfa77cb6692036188af77 + languageName: node + linkType: hard + +"@aws-crypto/sha1-browser@npm:5.2.0": + version: 5.2.0 + resolution: "@aws-crypto/sha1-browser@npm:5.2.0" + dependencies: + "@aws-crypto/supports-web-crypto": "npm:^5.2.0" + "@aws-crypto/util": "npm:^5.2.0" + "@aws-sdk/types": "npm:^3.222.0" + "@aws-sdk/util-locate-window": "npm:^3.0.0" + "@smithy/util-utf8": "npm:^2.0.0" + tslib: "npm:^2.6.2" + checksum: 10c0/51fed0bf078c10322d910af179871b7d299dde5b5897873ffbeeb036f427e5d11d23db9794439226544b73901920fd19f4d86bbc103ed73cc0cfdea47a83c6ac + languageName: node + linkType: hard + +"@aws-crypto/sha256-browser@npm:5.2.0": + version: 5.2.0 + resolution: "@aws-crypto/sha256-browser@npm:5.2.0" + dependencies: + "@aws-crypto/sha256-js": "npm:^5.2.0" + "@aws-crypto/supports-web-crypto": "npm:^5.2.0" + "@aws-crypto/util": "npm:^5.2.0" + "@aws-sdk/types": "npm:^3.222.0" + "@aws-sdk/util-locate-window": "npm:^3.0.0" + "@smithy/util-utf8": "npm:^2.0.0" + tslib: "npm:^2.6.2" + checksum: 10c0/05f6d256794df800fe9aef5f52f2ac7415f7f3117d461f85a6aecaa4e29e91527b6fd503681a17136fa89e9dd3d916e9c7e4cfb5eba222875cb6c077bdc1d00d + languageName: node + linkType: hard + +"@aws-crypto/sha256-js@npm:5.2.0, @aws-crypto/sha256-js@npm:^5.2.0": + version: 5.2.0 + resolution: "@aws-crypto/sha256-js@npm:5.2.0" + dependencies: + "@aws-crypto/util": "npm:^5.2.0" + "@aws-sdk/types": "npm:^3.222.0" + tslib: "npm:^2.6.2" + checksum: 10c0/6c48701f8336341bb104dfde3d0050c89c288051f6b5e9bdfeb8091cf3ffc86efcd5c9e6ff2a4a134406b019c07aca9db608128f8d9267c952578a3108db9fd1 + languageName: node + linkType: hard + +"@aws-crypto/supports-web-crypto@npm:^5.2.0": + version: 5.2.0 + resolution: "@aws-crypto/supports-web-crypto@npm:5.2.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/4d2118e29d68ca3f5947f1e37ce1fbb3239a0c569cc938cdc8ab8390d595609b5caf51a07c9e0535105b17bf5c52ea256fed705a07e9681118120ab64ee73af2 + languageName: node + linkType: hard + +"@aws-crypto/util@npm:^5.2.0": + version: 5.2.0 + resolution: "@aws-crypto/util@npm:5.2.0" + dependencies: + "@aws-sdk/types": "npm:^3.222.0" + "@smithy/util-utf8": "npm:^2.0.0" + tslib: "npm:^2.6.2" + checksum: 10c0/0362d4c197b1fd64b423966945130207d1fe23e1bb2878a18e361f7743c8d339dad3f8729895a29aa34fff6a86c65f281cf5167c4bf253f21627ae80b6dd2951 + languageName: node + linkType: hard + +"@aws-sdk/client-cloudfront@npm:^3.616.0": + version: 3.616.0 + resolution: "@aws-sdk/client-cloudfront@npm:3.616.0" + dependencies: + "@aws-crypto/sha256-browser": "npm:5.2.0" + "@aws-crypto/sha256-js": "npm:5.2.0" + "@aws-sdk/client-sso-oidc": "npm:3.616.0" + "@aws-sdk/client-sts": "npm:3.616.0" + "@aws-sdk/core": "npm:3.616.0" + "@aws-sdk/credential-provider-node": "npm:3.616.0" + "@aws-sdk/middleware-host-header": "npm:3.616.0" + "@aws-sdk/middleware-logger": "npm:3.609.0" + "@aws-sdk/middleware-recursion-detection": "npm:3.616.0" + "@aws-sdk/middleware-user-agent": "npm:3.616.0" + "@aws-sdk/region-config-resolver": "npm:3.614.0" + "@aws-sdk/types": "npm:3.609.0" + "@aws-sdk/util-endpoints": "npm:3.614.0" + "@aws-sdk/util-user-agent-browser": "npm:3.609.0" + "@aws-sdk/util-user-agent-node": "npm:3.614.0" + "@aws-sdk/xml-builder": "npm:3.609.0" + "@smithy/config-resolver": "npm:^3.0.5" + "@smithy/core": "npm:^2.2.7" + "@smithy/fetch-http-handler": "npm:^3.2.2" + "@smithy/hash-node": "npm:^3.0.3" + "@smithy/invalid-dependency": "npm:^3.0.3" + "@smithy/middleware-content-length": "npm:^3.0.4" + "@smithy/middleware-endpoint": "npm:^3.0.5" + "@smithy/middleware-retry": "npm:^3.0.10" + "@smithy/middleware-serde": "npm:^3.0.3" + "@smithy/middleware-stack": "npm:^3.0.3" + "@smithy/node-config-provider": "npm:^3.1.4" + "@smithy/node-http-handler": "npm:^3.1.3" + "@smithy/protocol-http": "npm:^4.0.4" + "@smithy/smithy-client": "npm:^3.1.8" + "@smithy/types": "npm:^3.3.0" + "@smithy/url-parser": "npm:^3.0.3" + "@smithy/util-base64": "npm:^3.0.0" + "@smithy/util-body-length-browser": "npm:^3.0.0" + "@smithy/util-body-length-node": "npm:^3.0.0" + "@smithy/util-defaults-mode-browser": "npm:^3.0.10" + "@smithy/util-defaults-mode-node": "npm:^3.0.10" + "@smithy/util-endpoints": "npm:^2.0.5" + "@smithy/util-middleware": "npm:^3.0.3" + "@smithy/util-retry": "npm:^3.0.3" + "@smithy/util-stream": "npm:^3.1.0" + "@smithy/util-utf8": "npm:^3.0.0" + "@smithy/util-waiter": "npm:^3.1.2" + tslib: "npm:^2.6.2" + checksum: 10c0/0457ecd9be8e495e43ece6eac78663927e8edb45383fd5b3421361c453e11c33509e21e705d4ba32b45f86938ab7a7d329cd67b0373a231e16d306c8cf866647 + languageName: node + linkType: hard + +"@aws-sdk/client-s3@npm:^3.616.0": + version: 3.616.0 + resolution: "@aws-sdk/client-s3@npm:3.616.0" + dependencies: + "@aws-crypto/sha1-browser": "npm:5.2.0" + "@aws-crypto/sha256-browser": "npm:5.2.0" + "@aws-crypto/sha256-js": "npm:5.2.0" + "@aws-sdk/client-sso-oidc": "npm:3.616.0" + "@aws-sdk/client-sts": "npm:3.616.0" + "@aws-sdk/core": "npm:3.616.0" + "@aws-sdk/credential-provider-node": "npm:3.616.0" + "@aws-sdk/middleware-bucket-endpoint": "npm:3.616.0" + "@aws-sdk/middleware-expect-continue": "npm:3.616.0" + "@aws-sdk/middleware-flexible-checksums": "npm:3.616.0" + "@aws-sdk/middleware-host-header": "npm:3.616.0" + "@aws-sdk/middleware-location-constraint": "npm:3.609.0" + "@aws-sdk/middleware-logger": "npm:3.609.0" + "@aws-sdk/middleware-recursion-detection": "npm:3.616.0" + "@aws-sdk/middleware-sdk-s3": "npm:3.616.0" + "@aws-sdk/middleware-signing": "npm:3.616.0" + "@aws-sdk/middleware-ssec": "npm:3.609.0" + "@aws-sdk/middleware-user-agent": "npm:3.616.0" + "@aws-sdk/region-config-resolver": "npm:3.614.0" + "@aws-sdk/signature-v4-multi-region": "npm:3.616.0" + "@aws-sdk/types": "npm:3.609.0" + "@aws-sdk/util-endpoints": "npm:3.614.0" + "@aws-sdk/util-user-agent-browser": "npm:3.609.0" + "@aws-sdk/util-user-agent-node": "npm:3.614.0" + "@aws-sdk/xml-builder": "npm:3.609.0" + "@smithy/config-resolver": "npm:^3.0.5" + "@smithy/core": "npm:^2.2.7" + "@smithy/eventstream-serde-browser": "npm:^3.0.4" + "@smithy/eventstream-serde-config-resolver": "npm:^3.0.3" + "@smithy/eventstream-serde-node": "npm:^3.0.4" + "@smithy/fetch-http-handler": "npm:^3.2.2" + "@smithy/hash-blob-browser": "npm:^3.1.2" + "@smithy/hash-node": "npm:^3.0.3" + "@smithy/hash-stream-node": "npm:^3.1.2" + "@smithy/invalid-dependency": "npm:^3.0.3" + "@smithy/md5-js": "npm:^3.0.3" + "@smithy/middleware-content-length": "npm:^3.0.4" + "@smithy/middleware-endpoint": "npm:^3.0.5" + "@smithy/middleware-retry": "npm:^3.0.10" + "@smithy/middleware-serde": "npm:^3.0.3" + "@smithy/middleware-stack": "npm:^3.0.3" + "@smithy/node-config-provider": "npm:^3.1.4" + "@smithy/node-http-handler": "npm:^3.1.3" + "@smithy/protocol-http": "npm:^4.0.4" + "@smithy/smithy-client": "npm:^3.1.8" + "@smithy/types": "npm:^3.3.0" + "@smithy/url-parser": "npm:^3.0.3" + "@smithy/util-base64": "npm:^3.0.0" + "@smithy/util-body-length-browser": "npm:^3.0.0" + "@smithy/util-body-length-node": "npm:^3.0.0" + "@smithy/util-defaults-mode-browser": "npm:^3.0.10" + "@smithy/util-defaults-mode-node": "npm:^3.0.10" + "@smithy/util-endpoints": "npm:^2.0.5" + "@smithy/util-retry": "npm:^3.0.3" + "@smithy/util-stream": "npm:^3.1.0" + "@smithy/util-utf8": "npm:^3.0.0" + "@smithy/util-waiter": "npm:^3.1.2" + tslib: "npm:^2.6.2" + checksum: 10c0/15e4e4364848dcf9fbee44104664659ea4d190a98f8d3f0e3fa2489474a09135c0646628193e7754dc2078bf09043e9a60467ae06448076de9ac566e4f8cf3d8 + languageName: node + linkType: hard + +"@aws-sdk/client-ses@npm:^3.616.0": + version: 3.616.0 + resolution: "@aws-sdk/client-ses@npm:3.616.0" + dependencies: + "@aws-crypto/sha256-browser": "npm:5.2.0" + "@aws-crypto/sha256-js": "npm:5.2.0" + "@aws-sdk/client-sso-oidc": "npm:3.616.0" + "@aws-sdk/client-sts": "npm:3.616.0" + "@aws-sdk/core": "npm:3.616.0" + "@aws-sdk/credential-provider-node": "npm:3.616.0" + "@aws-sdk/middleware-host-header": "npm:3.616.0" + "@aws-sdk/middleware-logger": "npm:3.609.0" + "@aws-sdk/middleware-recursion-detection": "npm:3.616.0" + "@aws-sdk/middleware-user-agent": "npm:3.616.0" + "@aws-sdk/region-config-resolver": "npm:3.614.0" + "@aws-sdk/types": "npm:3.609.0" + "@aws-sdk/util-endpoints": "npm:3.614.0" + "@aws-sdk/util-user-agent-browser": "npm:3.609.0" + "@aws-sdk/util-user-agent-node": "npm:3.614.0" + "@smithy/config-resolver": "npm:^3.0.5" + "@smithy/core": "npm:^2.2.7" + "@smithy/fetch-http-handler": "npm:^3.2.2" + "@smithy/hash-node": "npm:^3.0.3" + "@smithy/invalid-dependency": "npm:^3.0.3" + "@smithy/middleware-content-length": "npm:^3.0.4" + "@smithy/middleware-endpoint": "npm:^3.0.5" + "@smithy/middleware-retry": "npm:^3.0.10" + "@smithy/middleware-serde": "npm:^3.0.3" + "@smithy/middleware-stack": "npm:^3.0.3" + "@smithy/node-config-provider": "npm:^3.1.4" + "@smithy/node-http-handler": "npm:^3.1.3" + "@smithy/protocol-http": "npm:^4.0.4" + "@smithy/smithy-client": "npm:^3.1.8" + "@smithy/types": "npm:^3.3.0" + "@smithy/url-parser": "npm:^3.0.3" + "@smithy/util-base64": "npm:^3.0.0" + "@smithy/util-body-length-browser": "npm:^3.0.0" + "@smithy/util-body-length-node": "npm:^3.0.0" + "@smithy/util-defaults-mode-browser": "npm:^3.0.10" + "@smithy/util-defaults-mode-node": "npm:^3.0.10" + "@smithy/util-endpoints": "npm:^2.0.5" + "@smithy/util-middleware": "npm:^3.0.3" + "@smithy/util-retry": "npm:^3.0.3" + "@smithy/util-utf8": "npm:^3.0.0" + "@smithy/util-waiter": "npm:^3.1.2" + tslib: "npm:^2.6.2" + checksum: 10c0/13cac2418e12ea1a8551653c9489168693b1d26f61adf7c65084e36ae1277319e6d48ba25c273bf96ef3c3eea0fd5d3c8a5cfd8e18c39fdecae71d6a8b970310 + languageName: node + linkType: hard + +"@aws-sdk/client-sso-oidc@npm:3.616.0": + version: 3.616.0 + resolution: "@aws-sdk/client-sso-oidc@npm:3.616.0" + dependencies: + "@aws-crypto/sha256-browser": "npm:5.2.0" + "@aws-crypto/sha256-js": "npm:5.2.0" + "@aws-sdk/core": "npm:3.616.0" + "@aws-sdk/credential-provider-node": "npm:3.616.0" + "@aws-sdk/middleware-host-header": "npm:3.616.0" + "@aws-sdk/middleware-logger": "npm:3.609.0" + "@aws-sdk/middleware-recursion-detection": "npm:3.616.0" + "@aws-sdk/middleware-user-agent": "npm:3.616.0" + "@aws-sdk/region-config-resolver": "npm:3.614.0" + "@aws-sdk/types": "npm:3.609.0" + "@aws-sdk/util-endpoints": "npm:3.614.0" + "@aws-sdk/util-user-agent-browser": "npm:3.609.0" + "@aws-sdk/util-user-agent-node": "npm:3.614.0" + "@smithy/config-resolver": "npm:^3.0.5" + "@smithy/core": "npm:^2.2.7" + "@smithy/fetch-http-handler": "npm:^3.2.2" + "@smithy/hash-node": "npm:^3.0.3" + "@smithy/invalid-dependency": "npm:^3.0.3" + "@smithy/middleware-content-length": "npm:^3.0.4" + "@smithy/middleware-endpoint": "npm:^3.0.5" + "@smithy/middleware-retry": "npm:^3.0.10" + "@smithy/middleware-serde": "npm:^3.0.3" + "@smithy/middleware-stack": "npm:^3.0.3" + "@smithy/node-config-provider": "npm:^3.1.4" + "@smithy/node-http-handler": "npm:^3.1.3" + "@smithy/protocol-http": "npm:^4.0.4" + "@smithy/smithy-client": "npm:^3.1.8" + "@smithy/types": "npm:^3.3.0" + "@smithy/url-parser": "npm:^3.0.3" + "@smithy/util-base64": "npm:^3.0.0" + "@smithy/util-body-length-browser": "npm:^3.0.0" + "@smithy/util-body-length-node": "npm:^3.0.0" + "@smithy/util-defaults-mode-browser": "npm:^3.0.10" + "@smithy/util-defaults-mode-node": "npm:^3.0.10" + "@smithy/util-endpoints": "npm:^2.0.5" + "@smithy/util-middleware": "npm:^3.0.3" + "@smithy/util-retry": "npm:^3.0.3" + "@smithy/util-utf8": "npm:^3.0.0" + tslib: "npm:^2.6.2" + peerDependencies: + "@aws-sdk/client-sts": ^3.616.0 + checksum: 10c0/bc268f49eda21218eb8e8b2321bc7ff58670c8d361b83f5785f7b0fba7595cf95e170b5edafb637bd0af7024906b6b3fbcad73bd7ae32fb43b35fb60d1207b03 + languageName: node + linkType: hard + +"@aws-sdk/client-sso@npm:3.616.0": + version: 3.616.0 + resolution: "@aws-sdk/client-sso@npm:3.616.0" + dependencies: + "@aws-crypto/sha256-browser": "npm:5.2.0" + "@aws-crypto/sha256-js": "npm:5.2.0" + "@aws-sdk/core": "npm:3.616.0" + "@aws-sdk/middleware-host-header": "npm:3.616.0" + "@aws-sdk/middleware-logger": "npm:3.609.0" + "@aws-sdk/middleware-recursion-detection": "npm:3.616.0" + "@aws-sdk/middleware-user-agent": "npm:3.616.0" + "@aws-sdk/region-config-resolver": "npm:3.614.0" + "@aws-sdk/types": "npm:3.609.0" + "@aws-sdk/util-endpoints": "npm:3.614.0" + "@aws-sdk/util-user-agent-browser": "npm:3.609.0" + "@aws-sdk/util-user-agent-node": "npm:3.614.0" + "@smithy/config-resolver": "npm:^3.0.5" + "@smithy/core": "npm:^2.2.7" + "@smithy/fetch-http-handler": "npm:^3.2.2" + "@smithy/hash-node": "npm:^3.0.3" + "@smithy/invalid-dependency": "npm:^3.0.3" + "@smithy/middleware-content-length": "npm:^3.0.4" + "@smithy/middleware-endpoint": "npm:^3.0.5" + "@smithy/middleware-retry": "npm:^3.0.10" + "@smithy/middleware-serde": "npm:^3.0.3" + "@smithy/middleware-stack": "npm:^3.0.3" + "@smithy/node-config-provider": "npm:^3.1.4" + "@smithy/node-http-handler": "npm:^3.1.3" + "@smithy/protocol-http": "npm:^4.0.4" + "@smithy/smithy-client": "npm:^3.1.8" + "@smithy/types": "npm:^3.3.0" + "@smithy/url-parser": "npm:^3.0.3" + "@smithy/util-base64": "npm:^3.0.0" + "@smithy/util-body-length-browser": "npm:^3.0.0" + "@smithy/util-body-length-node": "npm:^3.0.0" + "@smithy/util-defaults-mode-browser": "npm:^3.0.10" + "@smithy/util-defaults-mode-node": "npm:^3.0.10" + "@smithy/util-endpoints": "npm:^2.0.5" + "@smithy/util-middleware": "npm:^3.0.3" + "@smithy/util-retry": "npm:^3.0.3" + "@smithy/util-utf8": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 10c0/8979602a4805485cb810cba125f429086a83f03ec258ead0191411abf6dbe3c1d76e2f0d81f229a95e55bbe30ad644f87dce88c19e69556783a04ccad595e3ac + languageName: node + linkType: hard + +"@aws-sdk/client-sts@npm:3.616.0": + version: 3.616.0 + resolution: "@aws-sdk/client-sts@npm:3.616.0" + dependencies: + "@aws-crypto/sha256-browser": "npm:5.2.0" + "@aws-crypto/sha256-js": "npm:5.2.0" + "@aws-sdk/client-sso-oidc": "npm:3.616.0" + "@aws-sdk/core": "npm:3.616.0" + "@aws-sdk/credential-provider-node": "npm:3.616.0" + "@aws-sdk/middleware-host-header": "npm:3.616.0" + "@aws-sdk/middleware-logger": "npm:3.609.0" + "@aws-sdk/middleware-recursion-detection": "npm:3.616.0" + "@aws-sdk/middleware-user-agent": "npm:3.616.0" + "@aws-sdk/region-config-resolver": "npm:3.614.0" + "@aws-sdk/types": "npm:3.609.0" + "@aws-sdk/util-endpoints": "npm:3.614.0" + "@aws-sdk/util-user-agent-browser": "npm:3.609.0" + "@aws-sdk/util-user-agent-node": "npm:3.614.0" + "@smithy/config-resolver": "npm:^3.0.5" + "@smithy/core": "npm:^2.2.7" + "@smithy/fetch-http-handler": "npm:^3.2.2" + "@smithy/hash-node": "npm:^3.0.3" + "@smithy/invalid-dependency": "npm:^3.0.3" + "@smithy/middleware-content-length": "npm:^3.0.4" + "@smithy/middleware-endpoint": "npm:^3.0.5" + "@smithy/middleware-retry": "npm:^3.0.10" + "@smithy/middleware-serde": "npm:^3.0.3" + "@smithy/middleware-stack": "npm:^3.0.3" + "@smithy/node-config-provider": "npm:^3.1.4" + "@smithy/node-http-handler": "npm:^3.1.3" + "@smithy/protocol-http": "npm:^4.0.4" + "@smithy/smithy-client": "npm:^3.1.8" + "@smithy/types": "npm:^3.3.0" + "@smithy/url-parser": "npm:^3.0.3" + "@smithy/util-base64": "npm:^3.0.0" + "@smithy/util-body-length-browser": "npm:^3.0.0" + "@smithy/util-body-length-node": "npm:^3.0.0" + "@smithy/util-defaults-mode-browser": "npm:^3.0.10" + "@smithy/util-defaults-mode-node": "npm:^3.0.10" + "@smithy/util-endpoints": "npm:^2.0.5" + "@smithy/util-middleware": "npm:^3.0.3" + "@smithy/util-retry": "npm:^3.0.3" + "@smithy/util-utf8": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 10c0/8dd321c511b60f8d2bbaa035bb6cbd176936443a523f0b20d752af35485679f279cfae86a8d0990c3eae8f790f4ed47f7857490cbd5bc1bfb0d79f6ff1519bf5 + languageName: node + linkType: hard + +"@aws-sdk/core@npm:3.616.0": + version: 3.616.0 + resolution: "@aws-sdk/core@npm:3.616.0" + dependencies: + "@smithy/core": "npm:^2.2.7" + "@smithy/protocol-http": "npm:^4.0.4" + "@smithy/signature-v4": "npm:^4.0.0" + "@smithy/smithy-client": "npm:^3.1.8" + "@smithy/types": "npm:^3.3.0" + fast-xml-parser: "npm:4.2.5" + tslib: "npm:^2.6.2" + checksum: 10c0/cd345067458a67ce637ac380dce3e86c7d0b6a512b69261c48f96deba0fe874868bf63ff96c9565deca31f4844b1dd54996c25279d0f6cb89b33a9479b5a0d78 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-env@npm:3.609.0": + version: 3.609.0 + resolution: "@aws-sdk/credential-provider-env@npm:3.609.0" + dependencies: + "@aws-sdk/types": "npm:3.609.0" + "@smithy/property-provider": "npm:^3.1.3" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/83a07a89113d6c89cfe95a8b3ed2118b251e8d34459dccf5a0ab60dbd55c72021a812dbd8b6df5762f05e6a93ab0c9dee3c558efef76316413401b82849080bb + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-http@npm:3.616.0": + version: 3.616.0 + resolution: "@aws-sdk/credential-provider-http@npm:3.616.0" + dependencies: + "@aws-sdk/types": "npm:3.609.0" + "@smithy/fetch-http-handler": "npm:^3.2.2" + "@smithy/node-http-handler": "npm:^3.1.3" + "@smithy/property-provider": "npm:^3.1.3" + "@smithy/protocol-http": "npm:^4.0.4" + "@smithy/smithy-client": "npm:^3.1.8" + "@smithy/types": "npm:^3.3.0" + "@smithy/util-stream": "npm:^3.1.0" + tslib: "npm:^2.6.2" + checksum: 10c0/92c1abd6b5a46a70346c27a9462460dd30b6fc6153f8b3c81df452a04f016b84fa425a90aa8b70c2ef596fae0fbca0a72da59a90e705b571685cc61bacb2459d + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-ini@npm:3.616.0": + version: 3.616.0 + resolution: "@aws-sdk/credential-provider-ini@npm:3.616.0" + dependencies: + "@aws-sdk/credential-provider-env": "npm:3.609.0" + "@aws-sdk/credential-provider-http": "npm:3.616.0" + "@aws-sdk/credential-provider-process": "npm:3.614.0" + "@aws-sdk/credential-provider-sso": "npm:3.616.0" + "@aws-sdk/credential-provider-web-identity": "npm:3.609.0" + "@aws-sdk/types": "npm:3.609.0" + "@smithy/credential-provider-imds": "npm:^3.1.4" + "@smithy/property-provider": "npm:^3.1.3" + "@smithy/shared-ini-file-loader": "npm:^3.1.4" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + peerDependencies: + "@aws-sdk/client-sts": ^3.616.0 + checksum: 10c0/36b6bfe4425f1e35768f12d13a50727fd186cd95d1a1ac68f097b38de319a8db0a1f9159caf2e634a88d95c71be00b28b65fc6f6d3fb016d4185a194ec31e3d5 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-node@npm:3.616.0": + version: 3.616.0 + resolution: "@aws-sdk/credential-provider-node@npm:3.616.0" + dependencies: + "@aws-sdk/credential-provider-env": "npm:3.609.0" + "@aws-sdk/credential-provider-http": "npm:3.616.0" + "@aws-sdk/credential-provider-ini": "npm:3.616.0" + "@aws-sdk/credential-provider-process": "npm:3.614.0" + "@aws-sdk/credential-provider-sso": "npm:3.616.0" + "@aws-sdk/credential-provider-web-identity": "npm:3.609.0" + "@aws-sdk/types": "npm:3.609.0" + "@smithy/credential-provider-imds": "npm:^3.1.4" + "@smithy/property-provider": "npm:^3.1.3" + "@smithy/shared-ini-file-loader": "npm:^3.1.4" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/f7e15a37a1608240d6bec4d5102a693b391300f3087e63a3d359e96e801dae1166926fac89c0015b17bd976fda795458199d859924554e146c66d207a1c43149 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-process@npm:3.614.0": + version: 3.614.0 + resolution: "@aws-sdk/credential-provider-process@npm:3.614.0" + dependencies: + "@aws-sdk/types": "npm:3.609.0" + "@smithy/property-provider": "npm:^3.1.3" + "@smithy/shared-ini-file-loader": "npm:^3.1.4" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/52c2c224b790bc76ad032acbe354ff284f32ba525080194c5aeed33cb0a588be03abf25243b669c054977259b633187d69cd6d4d7b2bb9b106fed3a44b7ec89c + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-sso@npm:3.616.0": + version: 3.616.0 + resolution: "@aws-sdk/credential-provider-sso@npm:3.616.0" + dependencies: + "@aws-sdk/client-sso": "npm:3.616.0" + "@aws-sdk/token-providers": "npm:3.614.0" + "@aws-sdk/types": "npm:3.609.0" + "@smithy/property-provider": "npm:^3.1.3" + "@smithy/shared-ini-file-loader": "npm:^3.1.4" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/09c5eef4db085b204444683a5ae3d48786375e303d6ce2b0156568bec473e102047f2299998c7218c282e8cdc3aee712728653c2ef7bbc4b108f6d741c5df5a3 + languageName: node + linkType: hard + +"@aws-sdk/credential-provider-web-identity@npm:3.609.0": + version: 3.609.0 + resolution: "@aws-sdk/credential-provider-web-identity@npm:3.609.0" + dependencies: + "@aws-sdk/types": "npm:3.609.0" + "@smithy/property-provider": "npm:^3.1.3" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + peerDependencies: + "@aws-sdk/client-sts": ^3.609.0 + checksum: 10c0/d7d2b9a82b5fe2c0001088e8772fb703b67474da908469bbbfa46964c99c79969e3fa6ccb28b2837e0c74a2fac391d14d53c1082b302b38d2410cb5b841f6900 + languageName: node + linkType: hard + +"@aws-sdk/middleware-bucket-endpoint@npm:3.616.0": + version: 3.616.0 + resolution: "@aws-sdk/middleware-bucket-endpoint@npm:3.616.0" + dependencies: + "@aws-sdk/types": "npm:3.609.0" + "@aws-sdk/util-arn-parser": "npm:3.568.0" + "@smithy/node-config-provider": "npm:^3.1.4" + "@smithy/protocol-http": "npm:^4.0.4" + "@smithy/types": "npm:^3.3.0" + "@smithy/util-config-provider": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 10c0/77d3b75395bd18b63e832356a8a9bf26059463242de32d1a6b7163c80a82f9970f118d4d4901ec7c04a601ef735bffe8a1cc60a640122ec7b6e681c7ea8d4525 + languageName: node + linkType: hard + +"@aws-sdk/middleware-expect-continue@npm:3.616.0": + version: 3.616.0 + resolution: "@aws-sdk/middleware-expect-continue@npm:3.616.0" + dependencies: + "@aws-sdk/types": "npm:3.609.0" + "@smithy/protocol-http": "npm:^4.0.4" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/bc90e337021d5aae00db1380546c5906816850ebc488ae373124d4a7830261b1e9f59a61186e211ff66904b06c3a8010875f6d9235e94f48d75a602273ab8e43 + languageName: node + linkType: hard + +"@aws-sdk/middleware-flexible-checksums@npm:3.616.0": + version: 3.616.0 + resolution: "@aws-sdk/middleware-flexible-checksums@npm:3.616.0" + dependencies: + "@aws-crypto/crc32": "npm:5.2.0" + "@aws-crypto/crc32c": "npm:5.2.0" + "@aws-sdk/types": "npm:3.609.0" + "@smithy/is-array-buffer": "npm:^3.0.0" + "@smithy/protocol-http": "npm:^4.0.4" + "@smithy/types": "npm:^3.3.0" + "@smithy/util-utf8": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 10c0/2c59282ea543fb657fb4a7b37ed81a94bcfb852e655035151e7a019d8e05f7b095e2787022fb8571adf63983ea3f99da240e3669f684ef325ead757a0dab18d9 + languageName: node + linkType: hard + +"@aws-sdk/middleware-host-header@npm:3.616.0": + version: 3.616.0 + resolution: "@aws-sdk/middleware-host-header@npm:3.616.0" + dependencies: + "@aws-sdk/types": "npm:3.609.0" + "@smithy/protocol-http": "npm:^4.0.4" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/5920eaf065827a66fe28572e8ea8501ddf2a44ce054484c8e9b3901f62a7dc1d70b69b2717797fba3fc41ad48d18a663b9d26391360b9571f7346f77eb6801f6 + languageName: node + linkType: hard + +"@aws-sdk/middleware-location-constraint@npm:3.609.0": + version: 3.609.0 + resolution: "@aws-sdk/middleware-location-constraint@npm:3.609.0" + dependencies: + "@aws-sdk/types": "npm:3.609.0" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/1eba2a3a1a003855a69e56f1c54fb2283b30db50bf14130cd042e25805497b7a19539144052c4fa710952d754d1a9e5d680fce09536509cf796a16816c8d506f + languageName: node + linkType: hard + +"@aws-sdk/middleware-logger@npm:3.609.0": + version: 3.609.0 + resolution: "@aws-sdk/middleware-logger@npm:3.609.0" + dependencies: + "@aws-sdk/types": "npm:3.609.0" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/e8d110552fee03c5290f94be8da8bb6c07404c06c68971cf24c89a5a4e08b93f6039a2bf729b173855815dd13e382eda18c31e098e7a40db9c8163b74a7770e7 + languageName: node + linkType: hard + +"@aws-sdk/middleware-recursion-detection@npm:3.616.0": + version: 3.616.0 + resolution: "@aws-sdk/middleware-recursion-detection@npm:3.616.0" + dependencies: + "@aws-sdk/types": "npm:3.609.0" + "@smithy/protocol-http": "npm:^4.0.4" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/e45203b7d3def07ccb9567fb7bacd4b341c6b80418abf73458b42fe60296e7a9636c17628abdc78f148a64a0704a6f288b5639436ff587381a7934443a94baf7 + languageName: node + linkType: hard + +"@aws-sdk/middleware-sdk-s3@npm:3.616.0": + version: 3.616.0 + resolution: "@aws-sdk/middleware-sdk-s3@npm:3.616.0" + dependencies: + "@aws-sdk/types": "npm:3.609.0" + "@aws-sdk/util-arn-parser": "npm:3.568.0" + "@smithy/node-config-provider": "npm:^3.1.4" + "@smithy/protocol-http": "npm:^4.0.4" + "@smithy/signature-v4": "npm:^4.0.0" + "@smithy/smithy-client": "npm:^3.1.8" + "@smithy/types": "npm:^3.3.0" + "@smithy/util-config-provider": "npm:^3.0.0" + "@smithy/util-stream": "npm:^3.1.0" + "@smithy/util-utf8": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 10c0/37cd1ec99285cf8a8ed5a5568e22a6bf1c9cee7c6c275a944031c6cf7127b44570a5f7d50cbd041a118947c5053a4846d12eefd7f9592c8c5aa5f486cd956ec1 + languageName: node + linkType: hard + +"@aws-sdk/middleware-signing@npm:3.616.0": + version: 3.616.0 + resolution: "@aws-sdk/middleware-signing@npm:3.616.0" + dependencies: + "@aws-sdk/types": "npm:3.609.0" + "@smithy/property-provider": "npm:^3.1.3" + "@smithy/protocol-http": "npm:^4.0.4" + "@smithy/signature-v4": "npm:^4.0.0" + "@smithy/types": "npm:^3.3.0" + "@smithy/util-middleware": "npm:^3.0.3" + tslib: "npm:^2.6.2" + checksum: 10c0/5f605347d5cc913341d327e48a1247a599b21408987d53b1b462d436ac8dfdfe1e8ab90ce918779af5cf1126ae8b4e3727a94c0e3f13ced7faf5607219931dc8 + languageName: node + linkType: hard + +"@aws-sdk/middleware-ssec@npm:3.609.0": + version: 3.609.0 + resolution: "@aws-sdk/middleware-ssec@npm:3.609.0" + dependencies: + "@aws-sdk/types": "npm:3.609.0" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/7688628299c3d3352182634836d8a5ad89d69dfedd91d7386ffeaa8288160329eef7d399321b7841bb4c84c9741d7245ef218657a8df71248b5ce5f7273e303d + languageName: node + linkType: hard + +"@aws-sdk/middleware-user-agent@npm:3.616.0": + version: 3.616.0 + resolution: "@aws-sdk/middleware-user-agent@npm:3.616.0" + dependencies: + "@aws-sdk/types": "npm:3.609.0" + "@aws-sdk/util-endpoints": "npm:3.614.0" + "@smithy/protocol-http": "npm:^4.0.4" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/831fbc0e8db7d76bf513d9493733f232af22bb9229a25c795f65351d4eccc9465cd88b8478155d501ee0811803ef094708983e3f02a4915e4b99f38acda837bc + languageName: node + linkType: hard + +"@aws-sdk/region-config-resolver@npm:3.614.0": + version: 3.614.0 + resolution: "@aws-sdk/region-config-resolver@npm:3.614.0" + dependencies: + "@aws-sdk/types": "npm:3.609.0" + "@smithy/node-config-provider": "npm:^3.1.4" + "@smithy/types": "npm:^3.3.0" + "@smithy/util-config-provider": "npm:^3.0.0" + "@smithy/util-middleware": "npm:^3.0.3" + tslib: "npm:^2.6.2" + checksum: 10c0/555842b34c26398741fa3a1f629d27d210270516b453b0a7237672a4472ff8e204c5979fe1823baddf4d695d4d95a631fadfa78d1d27089d9e9cba28e736346e + languageName: node + linkType: hard + +"@aws-sdk/signature-v4-multi-region@npm:3.616.0": + version: 3.616.0 + resolution: "@aws-sdk/signature-v4-multi-region@npm:3.616.0" + dependencies: + "@aws-sdk/middleware-sdk-s3": "npm:3.616.0" + "@aws-sdk/types": "npm:3.609.0" + "@smithy/protocol-http": "npm:^4.0.4" + "@smithy/signature-v4": "npm:^4.0.0" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/c18f5ac47791bdda6426bed17e245f2fa3368bec633d9d3e7f86d814e0fe091a85429f1c3d94644ac8f27b44f8547e24f5421e5b5beab648aad3ea622c259550 + languageName: node + linkType: hard + +"@aws-sdk/token-providers@npm:3.614.0": + version: 3.614.0 + resolution: "@aws-sdk/token-providers@npm:3.614.0" + dependencies: + "@aws-sdk/types": "npm:3.609.0" + "@smithy/property-provider": "npm:^3.1.3" + "@smithy/shared-ini-file-loader": "npm:^3.1.4" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + peerDependencies: + "@aws-sdk/client-sso-oidc": ^3.614.0 + checksum: 10c0/b794bcb9ad05f57bfc415e9290d3ea177701bb3221a9c5e1d4529deb946bd418acb7ac7407adb8d2f3da7d3793a62c7c1b43a8c1a8fe7999e38485208811f59a + languageName: node + linkType: hard + +"@aws-sdk/types@npm:3.609.0, @aws-sdk/types@npm:^3.222.0": + version: 3.609.0 + resolution: "@aws-sdk/types@npm:3.609.0" + dependencies: + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/293249118c2fc3cdc79ff9712e3a9f757a2f38e7d5d770507b3bb31d22b8c67ed6f9bdd83c1b6319236b8257d5cc7e2882c15e076200021e8bbf41e4780d430c + languageName: node + linkType: hard + +"@aws-sdk/util-arn-parser@npm:3.568.0": + version: 3.568.0 + resolution: "@aws-sdk/util-arn-parser@npm:3.568.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/4e6168b86a1ff4509f25b56e473c95bdcc0ecbaedcded29cbbd500eb7c156de63f2426282cd50489ac7f321a990056349974730f9e27ac3fe872ba3573b09fb6 + languageName: node + linkType: hard + +"@aws-sdk/util-endpoints@npm:3.614.0": + version: 3.614.0 + resolution: "@aws-sdk/util-endpoints@npm:3.614.0" + dependencies: + "@aws-sdk/types": "npm:3.609.0" + "@smithy/types": "npm:^3.3.0" + "@smithy/util-endpoints": "npm:^2.0.5" + tslib: "npm:^2.6.2" + checksum: 10c0/95a893dc3cff00d2ad5b48c4ffd83e19e45da75de7dd112b93b09f9e2a8db200e3a9ea7116b0fa943b945fb100f678795cbca1fb7be07bddcaac2549f6533332 + languageName: node + linkType: hard + +"@aws-sdk/util-locate-window@npm:^3.0.0": + version: 3.568.0 + resolution: "@aws-sdk/util-locate-window@npm:3.568.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/cb1d0919498206fe266542a635cd05909456a06f007a6a550ff897a01390b239e51c2a50e47509e23c179f8df8001bd5fecd900045da5ec989c3f934c3fd3d56 + languageName: node + linkType: hard + +"@aws-sdk/util-user-agent-browser@npm:3.609.0": + version: 3.609.0 + resolution: "@aws-sdk/util-user-agent-browser@npm:3.609.0" + dependencies: + "@aws-sdk/types": "npm:3.609.0" + "@smithy/types": "npm:^3.3.0" + bowser: "npm:^2.11.0" + tslib: "npm:^2.6.2" + checksum: 10c0/ca2f2863d753521fd63e0c924ed6f9602cc9f5bb65f7d0111be140d037962cf6897f49929dde21e4d8e613895486d9053abd8965d34a9a6ecc4a81de401f0f16 + languageName: node + linkType: hard + +"@aws-sdk/util-user-agent-node@npm:3.614.0": + version: 3.614.0 + resolution: "@aws-sdk/util-user-agent-node@npm:3.614.0" + dependencies: + "@aws-sdk/types": "npm:3.609.0" + "@smithy/node-config-provider": "npm:^3.1.4" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + peerDependencies: + aws-crt: ">=1.0.0" + peerDependenciesMeta: + aws-crt: + optional: true + checksum: 10c0/1e7b4d572a2915d921db814efbf771603b605aea114399aa357208433746f4b2990c927bdedd8616a6e50c98588032449b8994ce9ffae1cce7976986dc40adc1 + languageName: node + linkType: hard + +"@aws-sdk/xml-builder@npm:3.609.0": + version: 3.609.0 + resolution: "@aws-sdk/xml-builder@npm:3.609.0" + dependencies: + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/1d75f2dc7ff35557a1c437f108656574c737f0a9f9d0c91773cbdadbf3c42892e9305e1e1fd5b0c8b73520a902b1513d1a7d07864b964d6a369540ee23ad0ddb + languageName: node + linkType: hard + +"@babel/code-frame@npm:^7.0.0, @babel/code-frame@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/code-frame@npm:7.24.7" + dependencies: + "@babel/highlight": "npm:^7.24.7" + picocolors: "npm:^1.0.0" + checksum: 10c0/ab0af539473a9f5aeaac7047e377cb4f4edd255a81d84a76058595f8540784cc3fbe8acf73f1e073981104562490aabfb23008cd66dc677a456a4ed5390fdde6 + languageName: node + linkType: hard + +"@babel/compat-data@npm:^7.22.6, @babel/compat-data@npm:^7.24.8": + version: 7.24.9 + resolution: "@babel/compat-data@npm:7.24.9" + checksum: 10c0/95a69c9ed00ae78b4921f33403e9b35518e6139a0c46af763c65dea160720cb57c6cc23f7d30249091a0248335b0e39de5c8dfa8e7877c830e44561e0bdc1254 + languageName: node + linkType: hard + +"@babel/core@npm:^7.21.3": + version: 7.24.9 + resolution: "@babel/core@npm:7.24.9" + dependencies: + "@ampproject/remapping": "npm:^2.2.0" + "@babel/code-frame": "npm:^7.24.7" + "@babel/generator": "npm:^7.24.9" + "@babel/helper-compilation-targets": "npm:^7.24.8" + "@babel/helper-module-transforms": "npm:^7.24.9" + "@babel/helpers": "npm:^7.24.8" + "@babel/parser": "npm:^7.24.8" + "@babel/template": "npm:^7.24.7" + "@babel/traverse": "npm:^7.24.8" + "@babel/types": "npm:^7.24.9" + convert-source-map: "npm:^2.0.0" + debug: "npm:^4.1.0" + gensync: "npm:^1.0.0-beta.2" + json5: "npm:^2.2.3" + semver: "npm:^6.3.1" + checksum: 10c0/e104ec6efbf099f55184933e9ab078eb5821c792ddfef3e9c6561986ec4ff103f5c11e3d7d6e5e8929e50e2c58db1cc80e5b6f14b530335b6622095ec4b4124c + languageName: node + linkType: hard + +"@babel/generator@npm:^7.24.8, @babel/generator@npm:^7.24.9": + version: 7.24.10 + resolution: "@babel/generator@npm:7.24.10" + dependencies: + "@babel/types": "npm:^7.24.9" + "@jridgewell/gen-mapping": "npm:^0.3.5" + "@jridgewell/trace-mapping": "npm:^0.3.25" + jsesc: "npm:^2.5.1" + checksum: 10c0/abcfd75f625aecc87ce6036ef788b12723fd3c46530df1130d1f00d18e48b462849ddaeef8b1a02bfdcb6e28956389a98c5729dad1c3c5448307dacb6c959f29 + languageName: node + linkType: hard + +"@babel/helper-annotate-as-pure@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-annotate-as-pure@npm:7.24.7" + dependencies: + "@babel/types": "npm:^7.24.7" + checksum: 10c0/4679f7df4dffd5b3e26083ae65228116c3da34c3fff2c11ae11b259a61baec440f51e30fd236f7a0435b9d471acd93d0bc5a95df8213cbf02b1e083503d81b9a + languageName: node + linkType: hard + +"@babel/helper-builder-binary-assignment-operator-visitor@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-builder-binary-assignment-operator-visitor@npm:7.24.7" + dependencies: + "@babel/traverse": "npm:^7.24.7" + "@babel/types": "npm:^7.24.7" + checksum: 10c0/0ed84abf848c79fb1cd4c1ddac12c771d32c1904d87fc3087f33cfdeb0c2e0db4e7892b74b407d9d8d0c000044f3645a7391a781f788da8410c290bb123a1f13 + languageName: node + linkType: hard + +"@babel/helper-compilation-targets@npm:^7.22.6, @babel/helper-compilation-targets@npm:^7.24.7, @babel/helper-compilation-targets@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/helper-compilation-targets@npm:7.24.8" + dependencies: + "@babel/compat-data": "npm:^7.24.8" + "@babel/helper-validator-option": "npm:^7.24.8" + browserslist: "npm:^4.23.1" + lru-cache: "npm:^5.1.1" + semver: "npm:^6.3.1" + checksum: 10c0/2885c44ef6aaf82b7e4352b30089bb09fbe08ed5ec24eb452c2bdc3c021e2a65ab412f74b3d67ec1398da0356c730b33a2ceca1d67d34c85080d31ca6efa9aec + languageName: node + linkType: hard + +"@babel/helper-create-class-features-plugin@npm:^7.24.7, @babel/helper-create-class-features-plugin@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/helper-create-class-features-plugin@npm:7.24.8" + dependencies: + "@babel/helper-annotate-as-pure": "npm:^7.24.7" + "@babel/helper-environment-visitor": "npm:^7.24.7" + "@babel/helper-function-name": "npm:^7.24.7" + "@babel/helper-member-expression-to-functions": "npm:^7.24.8" + "@babel/helper-optimise-call-expression": "npm:^7.24.7" + "@babel/helper-replace-supers": "npm:^7.24.7" + "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.24.7" + "@babel/helper-split-export-declaration": "npm:^7.24.7" + semver: "npm:^6.3.1" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/e9abb3d73a3115accb29dc4854b9889545882486a2c4f8a44ff494000fca7aded298e9252ca0dd8aa9281c1abecc9524e5c67fa0e85d415728162a2d245fd2f5 + languageName: node + linkType: hard + +"@babel/helper-create-regexp-features-plugin@npm:^7.18.6, @babel/helper-create-regexp-features-plugin@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-create-regexp-features-plugin@npm:7.24.7" + dependencies: + "@babel/helper-annotate-as-pure": "npm:^7.24.7" + regexpu-core: "npm:^5.3.1" + semver: "npm:^6.3.1" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/ed611a7eb0c71843f9cdc471eeb38767972229f9225f7aaa90d124d7ee0062cf6908fd53ee9c34f731394c429594f06049a7738a71d342e0191d4047b2fc0ac2 + languageName: node + linkType: hard + +"@babel/helper-define-polyfill-provider@npm:^0.6.1, @babel/helper-define-polyfill-provider@npm:^0.6.2": + version: 0.6.2 + resolution: "@babel/helper-define-polyfill-provider@npm:0.6.2" + dependencies: + "@babel/helper-compilation-targets": "npm:^7.22.6" + "@babel/helper-plugin-utils": "npm:^7.22.5" + debug: "npm:^4.1.1" + lodash.debounce: "npm:^4.0.8" + resolve: "npm:^1.14.2" + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 10c0/f777fe0ee1e467fdaaac059c39ed203bdc94ef2465fb873316e9e1acfc511a276263724b061e3b0af2f6d7ad3ff174f2bb368fde236a860e0f650fda43d7e022 + languageName: node + linkType: hard + +"@babel/helper-environment-visitor@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-environment-visitor@npm:7.24.7" + dependencies: + "@babel/types": "npm:^7.24.7" + checksum: 10c0/36ece78882b5960e2d26abf13cf15ff5689bf7c325b10a2895a74a499e712de0d305f8d78bb382dd3c05cfba7e47ec98fe28aab5674243e0625cd38438dd0b2d + languageName: node + linkType: hard + +"@babel/helper-function-name@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-function-name@npm:7.24.7" + dependencies: + "@babel/template": "npm:^7.24.7" + "@babel/types": "npm:^7.24.7" + checksum: 10c0/e5e41e6cf86bd0f8bf272cbb6e7c5ee0f3e9660414174435a46653efba4f2479ce03ce04abff2aa2ef9359cf057c79c06cb7b134a565ad9c0e8a50dcdc3b43c4 + languageName: node + linkType: hard + +"@babel/helper-hoist-variables@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-hoist-variables@npm:7.24.7" + dependencies: + "@babel/types": "npm:^7.24.7" + checksum: 10c0/19ee37563bbd1219f9d98991ad0e9abef77803ee5945fd85aa7aa62a67c69efca9a801696a1b58dda27f211e878b3327789e6fd2a6f6c725ccefe36774b5ce95 + languageName: node + linkType: hard + +"@babel/helper-member-expression-to-functions@npm:^7.24.7, @babel/helper-member-expression-to-functions@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/helper-member-expression-to-functions@npm:7.24.8" + dependencies: + "@babel/traverse": "npm:^7.24.8" + "@babel/types": "npm:^7.24.8" + checksum: 10c0/7e14a5acc91f6cd26305a4441b82eb6f616bd70b096a4d2099a968f16b26d50207eec0b9ebfc466fefd62bd91587ac3be878117cdfec819b7151911183cb0e5a + languageName: node + linkType: hard + +"@babel/helper-module-imports@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-module-imports@npm:7.24.7" + dependencies: + "@babel/traverse": "npm:^7.24.7" + "@babel/types": "npm:^7.24.7" + checksum: 10c0/97c57db6c3eeaea31564286e328a9fb52b0313c5cfcc7eee4bc226aebcf0418ea5b6fe78673c0e4a774512ec6c86e309d0f326e99d2b37bfc16a25a032498af0 + languageName: node + linkType: hard + +"@babel/helper-module-transforms@npm:^7.24.7, @babel/helper-module-transforms@npm:^7.24.8, @babel/helper-module-transforms@npm:^7.24.9": + version: 7.24.9 + resolution: "@babel/helper-module-transforms@npm:7.24.9" + dependencies: + "@babel/helper-environment-visitor": "npm:^7.24.7" + "@babel/helper-module-imports": "npm:^7.24.7" + "@babel/helper-simple-access": "npm:^7.24.7" + "@babel/helper-split-export-declaration": "npm:^7.24.7" + "@babel/helper-validator-identifier": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/e27bca43bc113731ee4f2b33a4c5bf9c7eebf4d64487b814c305cbd5feb272c29fcd3d79634ba03131ade171e5972bc7ede8dbc83ba0deb02f1e62d318c87770 + languageName: node + linkType: hard + +"@babel/helper-optimise-call-expression@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-optimise-call-expression@npm:7.24.7" + dependencies: + "@babel/types": "npm:^7.24.7" + checksum: 10c0/ca6a9884705dea5c95a8b3ce132d1e3f2ae951ff74987d400d1d9c215dae9c0f9e29924d8f8e131e116533d182675bc261927be72f6a9a2968eaeeaa51eb1d0f + languageName: node + linkType: hard + +"@babel/helper-plugin-utils@npm:^7.0.0, @babel/helper-plugin-utils@npm:^7.10.4, @babel/helper-plugin-utils@npm:^7.12.13, @babel/helper-plugin-utils@npm:^7.14.5, @babel/helper-plugin-utils@npm:^7.18.6, @babel/helper-plugin-utils@npm:^7.22.5, @babel/helper-plugin-utils@npm:^7.24.7, @babel/helper-plugin-utils@npm:^7.24.8, @babel/helper-plugin-utils@npm:^7.8.0, @babel/helper-plugin-utils@npm:^7.8.3": + version: 7.24.8 + resolution: "@babel/helper-plugin-utils@npm:7.24.8" + checksum: 10c0/0376037f94a3bfe6b820a39f81220ac04f243eaee7193774b983e956c1750883ff236b30785795abbcda43fac3ece74750566830c2daa4d6e3870bb0dff34c2d + languageName: node + linkType: hard + +"@babel/helper-remap-async-to-generator@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-remap-async-to-generator@npm:7.24.7" + dependencies: + "@babel/helper-annotate-as-pure": "npm:^7.24.7" + "@babel/helper-environment-visitor": "npm:^7.24.7" + "@babel/helper-wrap-function": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/4e7fa2cdcbc488e41c27066c16e562857ef3c5c2bfe70d2f1e32e9ee7546b17c3fc1c20d05bf2a7f1c291bd9e7a0a219f6a9fa387209013294be79a26fcfe64d + languageName: node + linkType: hard + +"@babel/helper-replace-supers@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-replace-supers@npm:7.24.7" + dependencies: + "@babel/helper-environment-visitor": "npm:^7.24.7" + "@babel/helper-member-expression-to-functions": "npm:^7.24.7" + "@babel/helper-optimise-call-expression": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/0e133bb03371dee78e519c334a09c08e1493103a239d9628db0132dfaac3fc16380479ca3c590d278a9b71b624030a338c18ebbfe6d430ebb2e4653775c4b3e3 + languageName: node + linkType: hard + +"@babel/helper-simple-access@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-simple-access@npm:7.24.7" + dependencies: + "@babel/traverse": "npm:^7.24.7" + "@babel/types": "npm:^7.24.7" + checksum: 10c0/7230e419d59a85f93153415100a5faff23c133d7442c19e0cd070da1784d13cd29096ee6c5a5761065c44e8164f9f80e3a518c41a0256df39e38f7ad6744fed7 + languageName: node + linkType: hard + +"@babel/helper-skip-transparent-expression-wrappers@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-skip-transparent-expression-wrappers@npm:7.24.7" + dependencies: + "@babel/traverse": "npm:^7.24.7" + "@babel/types": "npm:^7.24.7" + checksum: 10c0/e3a9b8ac9c262ac976a1bcb5fe59694db5e6f0b4f9e7bdba5c7693b8b5e28113c23bdaa60fe8d3ec32a337091b67720b2053bcb3d5655f5406536c3d0584242b + languageName: node + linkType: hard + +"@babel/helper-split-export-declaration@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-split-export-declaration@npm:7.24.7" + dependencies: + "@babel/types": "npm:^7.24.7" + checksum: 10c0/0254577d7086bf09b01bbde98f731d4fcf4b7c3fa9634fdb87929801307c1f6202a1352e3faa5492450fa8da4420542d44de604daf540704ff349594a78184f6 + languageName: node + linkType: hard + +"@babel/helper-string-parser@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/helper-string-parser@npm:7.24.8" + checksum: 10c0/6361f72076c17fabf305e252bf6d580106429014b3ab3c1f5c4eb3e6d465536ea6b670cc0e9a637a77a9ad40454d3e41361a2909e70e305116a23d68ce094c08 + languageName: node + linkType: hard + +"@babel/helper-validator-identifier@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-validator-identifier@npm:7.24.7" + checksum: 10c0/87ad608694c9477814093ed5b5c080c2e06d44cb1924ae8320474a74415241223cc2a725eea2640dd783ff1e3390e5f95eede978bc540e870053152e58f1d651 + languageName: node + linkType: hard + +"@babel/helper-validator-option@npm:^7.24.7, @babel/helper-validator-option@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/helper-validator-option@npm:7.24.8" + checksum: 10c0/73db93a34ae89201351288bee7623eed81a54000779462a986105b54ffe82069e764afd15171a428b82e7c7a9b5fec10b5d5603b216317a414062edf5c67a21f + languageName: node + linkType: hard + +"@babel/helper-wrap-function@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/helper-wrap-function@npm:7.24.7" + dependencies: + "@babel/helper-function-name": "npm:^7.24.7" + "@babel/template": "npm:^7.24.7" + "@babel/traverse": "npm:^7.24.7" + "@babel/types": "npm:^7.24.7" + checksum: 10c0/d5689f031bf0eb38c0d7fad6b7e320ddef4bfbdf08d12d7d76ef41b7ca365a32721e74cb5ed5a9a9ec634bc20f9b7a27314fa6fb08f1576b8f6d8330fcea6f47 + languageName: node + linkType: hard + +"@babel/helpers@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/helpers@npm:7.24.8" + dependencies: + "@babel/template": "npm:^7.24.7" + "@babel/types": "npm:^7.24.8" + checksum: 10c0/42b8939b0a0bf72d6df9721973eb0fd7cd48f42641c5c9c740916397faa586255c06d36c6e6a7e091860723096281c620f6ffaee0011a3bb254a6f5475d89a12 + languageName: node + linkType: hard + +"@babel/highlight@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/highlight@npm:7.24.7" + dependencies: + "@babel/helper-validator-identifier": "npm:^7.24.7" + chalk: "npm:^2.4.2" + js-tokens: "npm:^4.0.0" + picocolors: "npm:^1.0.0" + checksum: 10c0/674334c571d2bb9d1c89bdd87566383f59231e16bcdcf5bb7835babdf03c9ae585ca0887a7b25bdf78f303984af028df52831c7989fecebb5101cc132da9393a + languageName: node + linkType: hard + +"@babel/parser@npm:^7.24.7, @babel/parser@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/parser@npm:7.24.8" + bin: + parser: ./bin/babel-parser.js + checksum: 10c0/ce69671de8fa6f649abf849be262707ac700b573b8b1ce1893c66cc6cd76aeb1294a19e8c290b0eadeb2f47d3f413a2e57a281804ffbe76bfb9fa50194cf3c52 + languageName: node + linkType: hard + +"@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-bugfix-firefox-class-in-computed-class-key@npm:7.24.7" + dependencies: + "@babel/helper-environment-visitor": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/394c30e2b708ad385fa1219528e039066a1f1cb40f47986f283878848fd354c745e6397f588b4e5a046ee8d64bfdf4c208e4c3dfbdcfb2fd34315ec67c64e7af + languageName: node + linkType: hard + +"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/a36307428ecc1a01b00cf90812335eed1575d13f211ab24fe4d0c55c28a2fcbd4135f142efabc3b277b2a8e09ee05df594a1272353f061b63829495b5dcfdb96 + languageName: node + linkType: hard + +"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.24.7" + "@babel/plugin-transform-optional-chaining": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.13.0 + checksum: 10c0/aeb6e7aa363a47f815cf956ea1053c5dd8b786a17799f065c9688ba4b0051fe7565d258bbe9400bfcbfb3114cb9fda66983e10afe4d750bc70ff75403e15dd36 + languageName: node + linkType: hard + +"@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@npm:7.24.7" + dependencies: + "@babel/helper-environment-visitor": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/2b52a73e444f6adc73f927b623e53a4cf64397170dd1071268536df1b3db1e02131418c8dc91351af48837a6298212118f4a72d5407f8005cf9a732370a315b0 + languageName: node + linkType: hard + +"@babel/plugin-proposal-private-property-in-object@npm:7.21.0-placeholder-for-preset-env.2": + version: 7.21.0-placeholder-for-preset-env.2 + resolution: "@babel/plugin-proposal-private-property-in-object@npm:7.21.0-placeholder-for-preset-env.2" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/e605e0070da087f6c35579499e65801179a521b6842c15181a1e305c04fded2393f11c1efd09b087be7f8b083d1b75e8f3efcbc1292b4f60d3369e14812cff63 + languageName: node + linkType: hard + +"@babel/plugin-syntax-async-generators@npm:^7.8.4": + version: 7.8.4 + resolution: "@babel/plugin-syntax-async-generators@npm:7.8.4" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.8.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/d13efb282838481348c71073b6be6245b35d4f2f964a8f71e4174f235009f929ef7613df25f8d2338e2d3e44bc4265a9f8638c6aaa136d7a61fe95985f9725c8 + languageName: node + linkType: hard + +"@babel/plugin-syntax-class-properties@npm:^7.12.13": + version: 7.12.13 + resolution: "@babel/plugin-syntax-class-properties@npm:7.12.13" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.12.13" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/95168fa186416195280b1264fb18afcdcdcea780b3515537b766cb90de6ce042d42dd6a204a39002f794ae5845b02afb0fd4861a3308a861204a55e68310a120 + languageName: node + linkType: hard + +"@babel/plugin-syntax-class-static-block@npm:^7.14.5": + version: 7.14.5 + resolution: "@babel/plugin-syntax-class-static-block@npm:7.14.5" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.14.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/4464bf9115f4a2d02ce1454411baf9cfb665af1da53709c5c56953e5e2913745b0fcce82982a00463d6facbdd93445c691024e310b91431a1e2f024b158f6371 + languageName: node + linkType: hard + +"@babel/plugin-syntax-dynamic-import@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-dynamic-import@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.8.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/9c50927bf71adf63f60c75370e2335879402648f468d0172bc912e303c6a3876927d8eb35807331b57f415392732ed05ab9b42c68ac30a936813ab549e0246c5 + languageName: node + linkType: hard + +"@babel/plugin-syntax-export-namespace-from@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-export-namespace-from@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.8.3" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/5100d658ba563829700cd8d001ddc09f4c0187b1a13de300d729c5b3e87503f75a6d6c99c1794182f7f1a9f546ee009df4f15a0ce36376e206ed0012fa7cdc24 + languageName: node + linkType: hard + +"@babel/plugin-syntax-import-assertions@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-syntax-import-assertions@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/b82c53e095274ee71c248551352d73441cf65b3b3fc0107258ba4e9aef7090772a425442b3ed1c396fa207d0efafde8929c87a17d3c885b3ca2021316e87e246 + languageName: node + linkType: hard + +"@babel/plugin-syntax-import-attributes@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-syntax-import-attributes@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/eccc54d0f03c96d0eec7a6e2fa124dadbc7298345b62ffc4238f173308c4325b5598f139695ff05a95cf78412ef6903599e4b814496612bf39aad4715a16375b + languageName: node + linkType: hard + +"@babel/plugin-syntax-import-meta@npm:^7.10.4": + version: 7.10.4 + resolution: "@babel/plugin-syntax-import-meta@npm:7.10.4" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.10.4" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/0b08b5e4c3128523d8e346f8cfc86824f0da2697b1be12d71af50a31aff7a56ceb873ed28779121051475010c28d6146a6bfea8518b150b71eeb4e46190172ee + languageName: node + linkType: hard + +"@babel/plugin-syntax-json-strings@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-json-strings@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.8.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/e98f31b2ec406c57757d115aac81d0336e8434101c224edd9a5c93cefa53faf63eacc69f3138960c8b25401315af03df37f68d316c151c4b933136716ed6906e + languageName: node + linkType: hard + +"@babel/plugin-syntax-jsx@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-syntax-jsx@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/f44d927a9ae8d5ef016ff5b450e1671e56629ddc12e56b938e41fd46e141170d9dfc9a53d6cb2b9a20a7dd266a938885e6a3981c60c052a2e1daed602ac80e51 + languageName: node + linkType: hard + +"@babel/plugin-syntax-logical-assignment-operators@npm:^7.10.4": + version: 7.10.4 + resolution: "@babel/plugin-syntax-logical-assignment-operators@npm:7.10.4" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.10.4" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/2594cfbe29411ad5bc2ad4058de7b2f6a8c5b86eda525a993959438615479e59c012c14aec979e538d60a584a1a799b60d1b8942c3b18468cb9d99b8fd34cd0b + languageName: node + linkType: hard + +"@babel/plugin-syntax-nullish-coalescing-operator@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-nullish-coalescing-operator@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.8.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/2024fbb1162899094cfc81152449b12bd0cc7053c6d4bda8ac2852545c87d0a851b1b72ed9560673cbf3ef6248257262c3c04aabf73117215c1b9cc7dd2542ce + languageName: node + linkType: hard + +"@babel/plugin-syntax-numeric-separator@npm:^7.10.4": + version: 7.10.4 + resolution: "@babel/plugin-syntax-numeric-separator@npm:7.10.4" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.10.4" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/c55a82b3113480942c6aa2fcbe976ff9caa74b7b1109ff4369641dfbc88d1da348aceb3c31b6ed311c84d1e7c479440b961906c735d0ab494f688bf2fd5b9bb9 + languageName: node + linkType: hard + +"@babel/plugin-syntax-object-rest-spread@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-object-rest-spread@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.8.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/ee1eab52ea6437e3101a0a7018b0da698545230015fc8ab129d292980ec6dff94d265e9e90070e8ae5fed42f08f1622c14c94552c77bcac784b37f503a82ff26 + languageName: node + linkType: hard + +"@babel/plugin-syntax-optional-catch-binding@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-optional-catch-binding@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.8.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/27e2493ab67a8ea6d693af1287f7e9acec206d1213ff107a928e85e173741e1d594196f99fec50e9dde404b09164f39dec5864c767212154ffe1caa6af0bc5af + languageName: node + linkType: hard + +"@babel/plugin-syntax-optional-chaining@npm:^7.8.3": + version: 7.8.3 + resolution: "@babel/plugin-syntax-optional-chaining@npm:7.8.3" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.8.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/46edddf2faa6ebf94147b8e8540dfc60a5ab718e2de4d01b2c0bdf250a4d642c2bd47cbcbb739febcb2bf75514dbcefad3c52208787994b8d0f8822490f55e81 + languageName: node + linkType: hard + +"@babel/plugin-syntax-private-property-in-object@npm:^7.14.5": + version: 7.14.5 + resolution: "@babel/plugin-syntax-private-property-in-object@npm:7.14.5" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.14.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/69822772561706c87f0a65bc92d0772cea74d6bc0911537904a676d5ff496a6d3ac4e05a166d8125fce4a16605bace141afc3611074e170a994e66e5397787f3 + languageName: node + linkType: hard + +"@babel/plugin-syntax-top-level-await@npm:^7.14.5": + version: 7.14.5 + resolution: "@babel/plugin-syntax-top-level-await@npm:7.14.5" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.14.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/14bf6e65d5bc1231ffa9def5f0ef30b19b51c218fcecaa78cd1bdf7939dfdf23f90336080b7f5196916368e399934ce5d581492d8292b46a2fb569d8b2da106f + languageName: node + linkType: hard + +"@babel/plugin-syntax-typescript@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-syntax-typescript@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/cdabd2e8010fb0ad15b49c2c270efc97c4bfe109ead36c7bbcf22da7a74bc3e49702fc4f22f12d2d6049e8e22a5769258df1fd05f0420ae45e11bdd5bc07805a + languageName: node + linkType: hard + +"@babel/plugin-syntax-unicode-sets-regex@npm:^7.18.6": + version: 7.18.6 + resolution: "@babel/plugin-syntax-unicode-sets-regex@npm:7.18.6" + dependencies: + "@babel/helper-create-regexp-features-plugin": "npm:^7.18.6" + "@babel/helper-plugin-utils": "npm:^7.18.6" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/9144e5b02a211a4fb9a0ce91063f94fbe1004e80bde3485a0910c9f14897cf83fabd8c21267907cff25db8e224858178df0517f14333cfcf3380ad9a4139cb50 + languageName: node + linkType: hard + +"@babel/plugin-transform-arrow-functions@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-arrow-functions@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/6ac05a54e5582f34ac6d5dc26499e227227ec1c7fa6fc8de1f3d40c275f140d3907f79bbbd49304da2d7008a5ecafb219d0b71d78ee3290ca22020d878041245 + languageName: node + linkType: hard + +"@babel/plugin-transform-async-generator-functions@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-async-generator-functions@npm:7.24.7" + dependencies: + "@babel/helper-environment-visitor": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-remap-async-to-generator": "npm:^7.24.7" + "@babel/plugin-syntax-async-generators": "npm:^7.8.4" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/6b5e33ae66dce0afce9b06d8dace6fa052528e60f7622aa6cfd3e71bd372ca5079d426e78336ca564bc0d5f37acbcda1b21f4fe656fcb642f1a93a697ab39742 + languageName: node + linkType: hard + +"@babel/plugin-transform-async-to-generator@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-async-to-generator@npm:7.24.7" + dependencies: + "@babel/helper-module-imports": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-remap-async-to-generator": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/83c82e243898875af8457972a26ab29baf8a2078768ee9f35141eb3edff0f84b165582a2ff73e90a9e08f5922bf813dbf15a85c1213654385198f4591c0dc45d + languageName: node + linkType: hard + +"@babel/plugin-transform-block-scoped-functions@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-block-scoped-functions@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/113e86de4612ae91773ff5cb6b980f01e1da7e26ae6f6012127415d7ae144e74987bc23feb97f63ba4bc699331490ddea36eac004d76a20d5369e4cc6a7f61cd + languageName: node + linkType: hard + +"@babel/plugin-transform-block-scoping@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-block-scoping@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/dcbc5e385c0ca5fb5736b1c720c90755cffe9f91d8c854f82e61e59217dd3f6c91b3633eeee4b55a89d3f59e5275d0f5b0b1b1363d4fa70c49c468b55aa87700 + languageName: node + linkType: hard + +"@babel/plugin-transform-class-properties@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-class-properties@npm:7.24.7" + dependencies: + "@babel/helper-create-class-features-plugin": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/75018a466c7ede3d2397e158891c224ba7fca72864506ce067ddbc02fc65191d44da4d6379c996d0c7f09019e26b5c3f5f1d3a639cd98366519723886f0689d0 + languageName: node + linkType: hard + +"@babel/plugin-transform-class-static-block@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-class-static-block@npm:7.24.7" + dependencies: + "@babel/helper-create-class-features-plugin": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/plugin-syntax-class-static-block": "npm:^7.14.5" + peerDependencies: + "@babel/core": ^7.12.0 + checksum: 10c0/b0ade39a3d09dce886f79dbd5907c3d99b48167eddb6b9bbde24a0598129654d7017e611c20494cdbea48b07ac14397cd97ea34e3754bbb2abae4e698128eccb + languageName: node + linkType: hard + +"@babel/plugin-transform-classes@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/plugin-transform-classes@npm:7.24.8" + dependencies: + "@babel/helper-annotate-as-pure": "npm:^7.24.7" + "@babel/helper-compilation-targets": "npm:^7.24.8" + "@babel/helper-environment-visitor": "npm:^7.24.7" + "@babel/helper-function-name": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.24.8" + "@babel/helper-replace-supers": "npm:^7.24.7" + "@babel/helper-split-export-declaration": "npm:^7.24.7" + globals: "npm:^11.1.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/4423da0f747bdb6aab1995d98a74533fa679f637ec20706810dd57fb4ba2b1885ec8cae6a0b2c3f69f27165de6ff6aa2da9c4061c893848736a8267d0c653079 + languageName: node + linkType: hard + +"@babel/plugin-transform-computed-properties@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-computed-properties@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/template": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/25636dbc1f605c0b8bc60aa58628a916b689473d11551c9864a855142e36742fe62d4a70400ba3b74902338e77fb3d940376c0a0ba154b6b7ec5367175233b49 + languageName: node + linkType: hard + +"@babel/plugin-transform-destructuring@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/plugin-transform-destructuring@npm:7.24.8" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.8" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/804968c1d5f5072c717505296c1e5d5ec33e90550423de66de82bbcb78157156e8470bbe77a04ab8c710a88a06360a30103cf223ac7eff4829adedd6150de5ce + languageName: node + linkType: hard + +"@babel/plugin-transform-dotall-regex@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-dotall-regex@npm:7.24.7" + dependencies: + "@babel/helper-create-regexp-features-plugin": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/793f14c9494972d294b7e7b97b747f47874b6d57d7804d3443c701becf5db192c9311be6a1835c07664486df1f5c60d33196c36fb7e11a53015e476b4c145b33 + languageName: node + linkType: hard + +"@babel/plugin-transform-duplicate-keys@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-duplicate-keys@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/75ff7ec1117ac500e77bf20a144411d39c0fdd038f108eec061724123ce6d1bb8d5bd27968e466573ee70014f8be0043361cdb0ef388f8a182d1d97ad67e51b9 + languageName: node + linkType: hard + +"@babel/plugin-transform-dynamic-import@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-dynamic-import@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/plugin-syntax-dynamic-import": "npm:^7.8.3" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/eeda48372efd0a5103cb22dadb13563c975bce18ae85daafbb47d57bb9665d187da9d4fe8d07ac0a6e1288afcfcb73e4e5618bf75ff63fddf9736bfbf225203b + languageName: node + linkType: hard + +"@babel/plugin-transform-exponentiation-operator@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-exponentiation-operator@npm:7.24.7" + dependencies: + "@babel/helper-builder-binary-assignment-operator-visitor": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/ace3e11c94041b88848552ba8feb39ae4d6cad3696d439ff51445bd2882d8b8775d85a26c2c0edb9b5e38c9e6013cc11b0dea89ec8f93c7d9d7ee95e3645078c + languageName: node + linkType: hard + +"@babel/plugin-transform-export-namespace-from@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-export-namespace-from@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/plugin-syntax-export-namespace-from": "npm:^7.8.3" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/4e144d7f1c57bc63b4899dbbbdfed0880f2daa75ea9c7251c7997f106e4b390dc362175ab7830f11358cb21f6b972ca10a43a2e56cd789065f7606b082674c0c + languageName: node + linkType: hard + +"@babel/plugin-transform-for-of@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-for-of@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/77629b1173e55d07416f05ba7353caa09d2c2149da2ca26721ab812209b63689d1be45116b68eadc011c49ced59daf5320835b15245eb7ae93ae0c5e8277cfc0 + languageName: node + linkType: hard + +"@babel/plugin-transform-function-name@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-function-name@npm:7.24.7" + dependencies: + "@babel/helper-compilation-targets": "npm:^7.24.7" + "@babel/helper-function-name": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/3e9642428d6952851850d89ea9307d55946528d18973784d0e2f04a651b23bd9924dd8a2641c824b483bd4ab1223bab1d2f6a1106a939998f7ced512cb60ac5b + languageName: node + linkType: hard + +"@babel/plugin-transform-json-strings@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-json-strings@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/plugin-syntax-json-strings": "npm:^7.8.3" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/17c72cd5bf3e90e722aabd333559275f3309e3fa0b9cea8c2944ab83ae01502c71a2be05da5101edc02b3fc8df15a8dbb9b861cbfcc8a52bf5e797cf01d3a40a + languageName: node + linkType: hard + +"@babel/plugin-transform-literals@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-literals@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/9f3f6f3831929cd2a977748c07addf9944d5cccb50bd3a24a58beb54f91f00d6cacd3d7831d13ffe1ad6f8aba0aefd7bca5aec65d63b77f39c62ad1f2d484a3e + languageName: node + linkType: hard + +"@babel/plugin-transform-logical-assignment-operators@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-logical-assignment-operators@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/plugin-syntax-logical-assignment-operators": "npm:^7.10.4" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/dbe882eb9053931f2ab332c50fc7c2a10ef507d6421bd9831adbb4cb7c9f8e1e5fbac4fbd2e007f6a1bf1df1843547559434012f118084dc0bf42cda3b106272 + languageName: node + linkType: hard + +"@babel/plugin-transform-member-expression-literals@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-member-expression-literals@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/e789ae359bdf2d20e90bedef18dfdbd965c9ebae1cee398474a0c349590fda7c8b874e1a2ceee62e47e5e6ec1730e76b0f24e502164357571854271fc12cc684 + languageName: node + linkType: hard + +"@babel/plugin-transform-modules-amd@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-modules-amd@npm:7.24.7" + dependencies: + "@babel/helper-module-transforms": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/6df7de7fce34117ca4b2fa07949b12274c03668cbfe21481c4037b6300796d50ae40f4f170527b61b70a67f26db906747797e30dbd0d9809a441b6e220b5728f + languageName: node + linkType: hard + +"@babel/plugin-transform-modules-commonjs@npm:^7.24.7, @babel/plugin-transform-modules-commonjs@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/plugin-transform-modules-commonjs@npm:7.24.8" + dependencies: + "@babel/helper-module-transforms": "npm:^7.24.8" + "@babel/helper-plugin-utils": "npm:^7.24.8" + "@babel/helper-simple-access": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/f1cf552307ebfced20d3907c1dd8be941b277f0364aa655e2b5fee828c84c54065745183104dae86f1f93ea0406db970a463ef7ceaaed897623748e99640e5a7 + languageName: node + linkType: hard + +"@babel/plugin-transform-modules-systemjs@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-modules-systemjs@npm:7.24.7" + dependencies: + "@babel/helper-hoist-variables": "npm:^7.24.7" + "@babel/helper-module-transforms": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-validator-identifier": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/e2a795e0a6baafe26f4a74010622212ddd873170742d673f450e0097f8d984f6e6a95eb8ce41b05071ee9790c4be088b33801aaab3f78ee202c567634e52a331 + languageName: node + linkType: hard + +"@babel/plugin-transform-modules-umd@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-modules-umd@npm:7.24.7" + dependencies: + "@babel/helper-module-transforms": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/7791d290121db210e4338b94b4a069a1a79e4c7a8d7638d8159a97b281851bbed3048dac87a4ae718ad963005e6c14a5d28e6db2eeb2b04e031cee92fb312f85 + languageName: node + linkType: hard + +"@babel/plugin-transform-named-capturing-groups-regex@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-named-capturing-groups-regex@npm:7.24.7" + dependencies: + "@babel/helper-create-regexp-features-plugin": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/41a0b0f2d0886318237440aa3b489f6d0305361d8671121777d9ff89f9f6de9d0c02ce93625049061426c8994064ef64deae8b819d1b14c00374a6a2336fb5d9 + languageName: node + linkType: hard + +"@babel/plugin-transform-new-target@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-new-target@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/2540808a35e1a978e537334c43dab439cf24c93e7beb213a2e71902f6710e60e0184316643790c0a6644e7a8021e52f7ab8165e6b3e2d6651be07bdf517b67df + languageName: node + linkType: hard + +"@babel/plugin-transform-nullish-coalescing-operator@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-nullish-coalescing-operator@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/plugin-syntax-nullish-coalescing-operator": "npm:^7.8.3" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/7243c8ff734ed5ef759dd8768773c4b443c12e792727e759a1aec2c7fa2bfdd24f1ecb42e292a7b3d8bd3d7f7b861cf256a8eb4ba144fc9cc463892c303083d9 + languageName: node + linkType: hard + +"@babel/plugin-transform-numeric-separator@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-numeric-separator@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/plugin-syntax-numeric-separator": "npm:^7.10.4" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/e18e09ca5a6342645d00ede477731aa6e8714ff357efc9d7cda5934f1703b3b6fb7d3298dce3ce3ba53e9ff1158eab8f1aadc68874cc21a6099d33a1ca457789 + languageName: node + linkType: hard + +"@babel/plugin-transform-object-rest-spread@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-object-rest-spread@npm:7.24.7" + dependencies: + "@babel/helper-compilation-targets": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/plugin-syntax-object-rest-spread": "npm:^7.8.3" + "@babel/plugin-transform-parameters": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/9ad64bc003f583030f9da50614b485852f8edac93f8faf5d1cd855201a4852f37c5255ae4daf70dd4375bdd4874e16e39b91f680d4668ec219ba05441ce286eb + languageName: node + linkType: hard + +"@babel/plugin-transform-object-super@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-object-super@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-replace-supers": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/770cebb4b4e1872c216b17069db9a13b87dfee747d359dc56d9fcdd66e7544f92dc6ab1861a4e7e0528196aaff2444e4f17dc84efd8eaf162d542b4ba0943869 + languageName: node + linkType: hard + +"@babel/plugin-transform-optional-catch-binding@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-optional-catch-binding@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/plugin-syntax-optional-catch-binding": "npm:^7.8.3" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/1e2f10a018f7d03b3bde6c0b70d063df8d5dd5209861d4467726cf834f5e3d354e2276079dc226aa8e6ece35f5c9b264d64b8229a8bb232829c01e561bcfb07a + languageName: node + linkType: hard + +"@babel/plugin-transform-optional-chaining@npm:^7.24.7, @babel/plugin-transform-optional-chaining@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/plugin-transform-optional-chaining@npm:7.24.8" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.8" + "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.24.7" + "@babel/plugin-syntax-optional-chaining": "npm:^7.8.3" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/4ffbe1aad7dec7c9aa2bf6ceb4b2f91f96815b2784f2879bde80e46934f59d64a12cb2c6262e40897c4754d77d2c35d8a5cfed63044fdebf94978b1ed3d14b17 + languageName: node + linkType: hard + +"@babel/plugin-transform-parameters@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-parameters@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/53bf190d6926771545d5184f1f5f3f5144d0f04f170799ad46a43f683a01fab8d5fe4d2196cf246774530990c31fe1f2b9f0def39f0a5ddbb2340b924f5edf01 + languageName: node + linkType: hard + +"@babel/plugin-transform-private-methods@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-private-methods@npm:7.24.7" + dependencies: + "@babel/helper-create-class-features-plugin": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/5b7bf923b738fbe3ad6c33b260e0a7451be288edfe4ef516303fa787a1870cd87533bfbf61abb779c22ed003c2fc484dec2436fe75a48756f686c0241173d364 + languageName: node + linkType: hard + +"@babel/plugin-transform-private-property-in-object@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-private-property-in-object@npm:7.24.7" + dependencies: + "@babel/helper-annotate-as-pure": "npm:^7.24.7" + "@babel/helper-create-class-features-plugin": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/plugin-syntax-private-property-in-object": "npm:^7.14.5" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/c6fa7defb90b1b0ed46f24ff94ff2e77f44c1f478d1090e81712f33cf992dda5ba347016f030082a2f770138bac6f4a9c2c1565e9f767a125901c77dd9c239ba + languageName: node + linkType: hard + +"@babel/plugin-transform-property-literals@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-property-literals@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/52564b58f3d111dc02d241d5892a4b01512e98dfdf6ef11b0ed62f8b11b0acacccef0fc229b44114fe8d1a57a8b70780b11bdd18b807d3754a781a07d8f57433 + languageName: node + linkType: hard + +"@babel/plugin-transform-react-constant-elements@npm:^7.21.3": + version: 7.24.7 + resolution: "@babel/plugin-transform-react-constant-elements@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/4b7a7314c4492d5ea0d0d705e76065e669f63812fe6f61588168d71a0f3c99f1bcaac22cdd09d71a56d951cf5ea0aec0b4b49717fd51db919b49e14f9a29503a + languageName: node + linkType: hard + +"@babel/plugin-transform-react-display-name@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-react-display-name@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/c14a07a9e75723c96f1a0a306b8a8e899ff1c6a0cc3d62bcda79bb1b54e4319127b258651c513a1a47da152cdc22e16525525a30ae5933a2980c7036fd0b4d24 + languageName: node + linkType: hard + +"@babel/plugin-transform-react-jsx-development@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-react-jsx-development@npm:7.24.7" + dependencies: + "@babel/plugin-transform-react-jsx": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/fce647db50f90a5291681f0f97865d9dc76981262dff71d6d0332e724b85343de5860c26f9e9a79e448d61e1d70916b07ce91e8c7f2b80dceb4b16aee41794d8 + languageName: node + linkType: hard + +"@babel/plugin-transform-react-jsx@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-react-jsx@npm:7.24.7" + dependencies: + "@babel/helper-annotate-as-pure": "npm:^7.24.7" + "@babel/helper-module-imports": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/plugin-syntax-jsx": "npm:^7.24.7" + "@babel/types": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/5c46d2c1c06a30e6bde084839df9cc689bf9c9cb0292105d61c225ca731f64247990724caee7dfc7f817dc964c062e8319e7f05394209590c476b65d75373435 + languageName: node + linkType: hard + +"@babel/plugin-transform-react-pure-annotations@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-react-pure-annotations@npm:7.24.7" + dependencies: + "@babel/helper-annotate-as-pure": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/fae517d293d9c93b7b920458c3e4b91cb0400513889af41ba184a5f3acc8bfef27242cc262741bb8f87870df376f1733a0d0f52b966d342e2aaaf5607af8f73d + languageName: node + linkType: hard + +"@babel/plugin-transform-regenerator@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-regenerator@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + regenerator-transform: "npm:^0.15.2" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/d2dc2c788fdae9d97217e70d46ba8ca9db0035c398dc3e161552b0c437113719a75c04f201f9c91ddc8d28a1da60d0b0853f616dead98a396abb9c845c44892b + languageName: node + linkType: hard + +"@babel/plugin-transform-reserved-words@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-reserved-words@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/2229de2768615e7f5dc0bbc55bc121b5678fd6d2febd46c74a58e42bb894d74cd5955c805880f4e02d0e1cf94f6886270eda7fafc1be9305a1ec3b9fd1d063f5 + languageName: node + linkType: hard + +"@babel/plugin-transform-shorthand-properties@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-shorthand-properties@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/41b155bdbb3be66618358488bf7731b3b2e8fff2de3dbfd541847720a9debfcec14db06a117abedd03c9cd786db20a79e2a86509a4f19513f6e1b610520905cf + languageName: node + linkType: hard + +"@babel/plugin-transform-spread@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-spread@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-skip-transparent-expression-wrappers": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/facba1553035f76b0d2930d4ada89a8cd0f45b79579afd35baefbfaf12e3b86096995f4b0c402cf9ee23b3f2ea0a4460c3b1ec0c192d340962c948bb223d4e66 + languageName: node + linkType: hard + +"@babel/plugin-transform-sticky-regex@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-sticky-regex@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/5a74ed2ed0a3ab51c3d15fcaf09d9e2fe915823535c7a4d7b019813177d559b69677090e189ec3d5d08b619483eb5ad371fbcfbbff5ace2a76ba33ee566a1109 + languageName: node + linkType: hard + +"@babel/plugin-transform-template-literals@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-template-literals@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/3630f966257bcace122f04d3157416a09d40768c44c3a800855da81146b009187daa21859d1c3b7d13f4e19e8888e60613964b175b2275d451200fb6d8d6cfe6 + languageName: node + linkType: hard + +"@babel/plugin-transform-typeof-symbol@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/plugin-transform-typeof-symbol@npm:7.24.8" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.8" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/2f570a4fbbdc5fd85f48165a97452826560051e3b8efb48c3bb0a0a33ee8485633439e7b71bfe3ef705583a1df43f854f49125bd759abdedc195b2cf7e60012a + languageName: node + linkType: hard + +"@babel/plugin-transform-typescript@npm:^7.24.7": + version: 7.24.8 + resolution: "@babel/plugin-transform-typescript@npm:7.24.8" + dependencies: + "@babel/helper-annotate-as-pure": "npm:^7.24.7" + "@babel/helper-create-class-features-plugin": "npm:^7.24.8" + "@babel/helper-plugin-utils": "npm:^7.24.8" + "@babel/plugin-syntax-typescript": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/bb3935b2e50bf4a6baba278840cee95f7274f15a1c919fb414f64dd4172a867e85345aea511ccfaa08fae17cb307e8b64580365c74a651057283bc17dff0e169 + languageName: node + linkType: hard + +"@babel/plugin-transform-unicode-escapes@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-unicode-escapes@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/8b18e2e66af33471a6971289492beff5c240e56727331db1d34c4338a6a368a82a7ed6d57ec911001b6d65643aed76531e1e7cac93265fb3fb2717f54d845e69 + languageName: node + linkType: hard + +"@babel/plugin-transform-unicode-property-regex@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-unicode-property-regex@npm:7.24.7" + dependencies: + "@babel/helper-create-regexp-features-plugin": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/bc57656eb94584d1b74a385d378818ac2b3fca642e3f649fead8da5fb3f9de22f8461185936915dfb33d5a9104e62e7a47828331248b09d28bb2d59e9276de3e + languageName: node + linkType: hard + +"@babel/plugin-transform-unicode-regex@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-unicode-regex@npm:7.24.7" + dependencies: + "@babel/helper-create-regexp-features-plugin": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/83f72a345b751566b601dc4d07e9f2c8f1bc0e0c6f7abb56ceb3095b3c9d304de73f85f2f477a09f8cc7edd5e65afd0ff9e376cdbcbea33bc0c28f3705b38fd9 + languageName: node + linkType: hard + +"@babel/plugin-transform-unicode-sets-regex@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/plugin-transform-unicode-sets-regex@npm:7.24.7" + dependencies: + "@babel/helper-create-regexp-features-plugin": "npm:^7.24.7" + "@babel/helper-plugin-utils": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0 + checksum: 10c0/7457c0ee8e80a80cb6fdc1fe54ab115b52815627616ce9151be8ef292fc99d04a910ec24f11382b4f124b89374264396892b086886bd2a9c2317904d87c9b21b + languageName: node + linkType: hard + +"@babel/preset-env@npm:^7.20.2": + version: 7.24.8 + resolution: "@babel/preset-env@npm:7.24.8" + dependencies: + "@babel/compat-data": "npm:^7.24.8" + "@babel/helper-compilation-targets": "npm:^7.24.8" + "@babel/helper-plugin-utils": "npm:^7.24.8" + "@babel/helper-validator-option": "npm:^7.24.8" + "@babel/plugin-bugfix-firefox-class-in-computed-class-key": "npm:^7.24.7" + "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": "npm:^7.24.7" + "@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining": "npm:^7.24.7" + "@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly": "npm:^7.24.7" + "@babel/plugin-proposal-private-property-in-object": "npm:7.21.0-placeholder-for-preset-env.2" + "@babel/plugin-syntax-async-generators": "npm:^7.8.4" + "@babel/plugin-syntax-class-properties": "npm:^7.12.13" + "@babel/plugin-syntax-class-static-block": "npm:^7.14.5" + "@babel/plugin-syntax-dynamic-import": "npm:^7.8.3" + "@babel/plugin-syntax-export-namespace-from": "npm:^7.8.3" + "@babel/plugin-syntax-import-assertions": "npm:^7.24.7" + "@babel/plugin-syntax-import-attributes": "npm:^7.24.7" + "@babel/plugin-syntax-import-meta": "npm:^7.10.4" + "@babel/plugin-syntax-json-strings": "npm:^7.8.3" + "@babel/plugin-syntax-logical-assignment-operators": "npm:^7.10.4" + "@babel/plugin-syntax-nullish-coalescing-operator": "npm:^7.8.3" + "@babel/plugin-syntax-numeric-separator": "npm:^7.10.4" + "@babel/plugin-syntax-object-rest-spread": "npm:^7.8.3" + "@babel/plugin-syntax-optional-catch-binding": "npm:^7.8.3" + "@babel/plugin-syntax-optional-chaining": "npm:^7.8.3" + "@babel/plugin-syntax-private-property-in-object": "npm:^7.14.5" + "@babel/plugin-syntax-top-level-await": "npm:^7.14.5" + "@babel/plugin-syntax-unicode-sets-regex": "npm:^7.18.6" + "@babel/plugin-transform-arrow-functions": "npm:^7.24.7" + "@babel/plugin-transform-async-generator-functions": "npm:^7.24.7" + "@babel/plugin-transform-async-to-generator": "npm:^7.24.7" + "@babel/plugin-transform-block-scoped-functions": "npm:^7.24.7" + "@babel/plugin-transform-block-scoping": "npm:^7.24.7" + "@babel/plugin-transform-class-properties": "npm:^7.24.7" + "@babel/plugin-transform-class-static-block": "npm:^7.24.7" + "@babel/plugin-transform-classes": "npm:^7.24.8" + "@babel/plugin-transform-computed-properties": "npm:^7.24.7" + "@babel/plugin-transform-destructuring": "npm:^7.24.8" + "@babel/plugin-transform-dotall-regex": "npm:^7.24.7" + "@babel/plugin-transform-duplicate-keys": "npm:^7.24.7" + "@babel/plugin-transform-dynamic-import": "npm:^7.24.7" + "@babel/plugin-transform-exponentiation-operator": "npm:^7.24.7" + "@babel/plugin-transform-export-namespace-from": "npm:^7.24.7" + "@babel/plugin-transform-for-of": "npm:^7.24.7" + "@babel/plugin-transform-function-name": "npm:^7.24.7" + "@babel/plugin-transform-json-strings": "npm:^7.24.7" + "@babel/plugin-transform-literals": "npm:^7.24.7" + "@babel/plugin-transform-logical-assignment-operators": "npm:^7.24.7" + "@babel/plugin-transform-member-expression-literals": "npm:^7.24.7" + "@babel/plugin-transform-modules-amd": "npm:^7.24.7" + "@babel/plugin-transform-modules-commonjs": "npm:^7.24.8" + "@babel/plugin-transform-modules-systemjs": "npm:^7.24.7" + "@babel/plugin-transform-modules-umd": "npm:^7.24.7" + "@babel/plugin-transform-named-capturing-groups-regex": "npm:^7.24.7" + "@babel/plugin-transform-new-target": "npm:^7.24.7" + "@babel/plugin-transform-nullish-coalescing-operator": "npm:^7.24.7" + "@babel/plugin-transform-numeric-separator": "npm:^7.24.7" + "@babel/plugin-transform-object-rest-spread": "npm:^7.24.7" + "@babel/plugin-transform-object-super": "npm:^7.24.7" + "@babel/plugin-transform-optional-catch-binding": "npm:^7.24.7" + "@babel/plugin-transform-optional-chaining": "npm:^7.24.8" + "@babel/plugin-transform-parameters": "npm:^7.24.7" + "@babel/plugin-transform-private-methods": "npm:^7.24.7" + "@babel/plugin-transform-private-property-in-object": "npm:^7.24.7" + "@babel/plugin-transform-property-literals": "npm:^7.24.7" + "@babel/plugin-transform-regenerator": "npm:^7.24.7" + "@babel/plugin-transform-reserved-words": "npm:^7.24.7" + "@babel/plugin-transform-shorthand-properties": "npm:^7.24.7" + "@babel/plugin-transform-spread": "npm:^7.24.7" + "@babel/plugin-transform-sticky-regex": "npm:^7.24.7" + "@babel/plugin-transform-template-literals": "npm:^7.24.7" + "@babel/plugin-transform-typeof-symbol": "npm:^7.24.8" + "@babel/plugin-transform-unicode-escapes": "npm:^7.24.7" + "@babel/plugin-transform-unicode-property-regex": "npm:^7.24.7" + "@babel/plugin-transform-unicode-regex": "npm:^7.24.7" + "@babel/plugin-transform-unicode-sets-regex": "npm:^7.24.7" + "@babel/preset-modules": "npm:0.1.6-no-external-plugins" + babel-plugin-polyfill-corejs2: "npm:^0.4.10" + babel-plugin-polyfill-corejs3: "npm:^0.10.4" + babel-plugin-polyfill-regenerator: "npm:^0.6.1" + core-js-compat: "npm:^3.37.1" + semver: "npm:^6.3.1" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/a6f29498ec58989845a61f9c10b1b4e80586f1810a33db461d597cdb0ad2cd847381a993038b09f727512a08b2c1a33a330a5d4e6d65463ee98a1b4302d52ec6 + languageName: node + linkType: hard + +"@babel/preset-modules@npm:0.1.6-no-external-plugins": + version: 0.1.6-no-external-plugins + resolution: "@babel/preset-modules@npm:0.1.6-no-external-plugins" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.0.0" + "@babel/types": "npm:^7.4.4" + esutils: "npm:^2.0.2" + peerDependencies: + "@babel/core": ^7.0.0-0 || ^8.0.0-0 <8.0.0 + checksum: 10c0/9d02f70d7052446c5f3a4fb39e6b632695fb6801e46d31d7f7c5001f7c18d31d1ea8369212331ca7ad4e7877b73231f470b0d559162624128f1b80fe591409e6 + languageName: node + linkType: hard + +"@babel/preset-react@npm:^7.18.6": + version: 7.24.7 + resolution: "@babel/preset-react@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-validator-option": "npm:^7.24.7" + "@babel/plugin-transform-react-display-name": "npm:^7.24.7" + "@babel/plugin-transform-react-jsx": "npm:^7.24.7" + "@babel/plugin-transform-react-jsx-development": "npm:^7.24.7" + "@babel/plugin-transform-react-pure-annotations": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/9658b685b25cedaadd0b65c4e663fbc7f57394b5036ddb4c99b1a75b0711fb83292c1c625d605c05b73413fc7a6dc20e532627f6a39b6dc8d4e00415479b054c + languageName: node + linkType: hard + +"@babel/preset-typescript@npm:^7.21.0": + version: 7.24.7 + resolution: "@babel/preset-typescript@npm:7.24.7" + dependencies: + "@babel/helper-plugin-utils": "npm:^7.24.7" + "@babel/helper-validator-option": "npm:^7.24.7" + "@babel/plugin-syntax-jsx": "npm:^7.24.7" + "@babel/plugin-transform-modules-commonjs": "npm:^7.24.7" + "@babel/plugin-transform-typescript": "npm:^7.24.7" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/986bc0978eedb4da33aba8e1e13a3426dd1829515313b7e8f4ba5d8c18aff1663b468939d471814e7acf4045d326ae6cff37239878d169ac3fe53a8fde71f8ee + languageName: node + linkType: hard + +"@babel/regjsgen@npm:^0.8.0": + version: 0.8.0 + resolution: "@babel/regjsgen@npm:0.8.0" + checksum: 10c0/4f3ddd8c7c96d447e05c8304c1d5ba3a83fcabd8a716bc1091c2f31595cdd43a3a055fff7cb5d3042b8cb7d402d78820fcb4e05d896c605a7d8bcf30f2424c4a + languageName: node + linkType: hard + +"@babel/runtime@npm:^7.23.9, @babel/runtime@npm:^7.3.1, @babel/runtime@npm:^7.5.5, @babel/runtime@npm:^7.8.4, @babel/runtime@npm:^7.8.7": + version: 7.24.8 + resolution: "@babel/runtime@npm:7.24.8" + dependencies: + regenerator-runtime: "npm:^0.14.0" + checksum: 10c0/f24b30af6b3ecae19165b3b032f9bc37b2d1769677bd63b69a6f81061967cfc847aa822518402ea6616b1d301d7eb46986b99c9f69cdb5880834fca2e6b34881 + languageName: node + linkType: hard + +"@babel/template@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/template@npm:7.24.7" + dependencies: + "@babel/code-frame": "npm:^7.24.7" + "@babel/parser": "npm:^7.24.7" + "@babel/types": "npm:^7.24.7" + checksum: 10c0/95b0b3ee80fcef685b7f4426f5713a855ea2cd5ac4da829b213f8fb5afe48a2a14683c2ea04d446dbc7f711c33c5cd4a965ef34dcbe5bc387c9e966b67877ae3 + languageName: node + linkType: hard + +"@babel/traverse@npm:^7.24.7, @babel/traverse@npm:^7.24.8": + version: 7.24.8 + resolution: "@babel/traverse@npm:7.24.8" + dependencies: + "@babel/code-frame": "npm:^7.24.7" + "@babel/generator": "npm:^7.24.8" + "@babel/helper-environment-visitor": "npm:^7.24.7" + "@babel/helper-function-name": "npm:^7.24.7" + "@babel/helper-hoist-variables": "npm:^7.24.7" + "@babel/helper-split-export-declaration": "npm:^7.24.7" + "@babel/parser": "npm:^7.24.8" + "@babel/types": "npm:^7.24.8" + debug: "npm:^4.3.1" + globals: "npm:^11.1.0" + checksum: 10c0/67a5cc35824455cdb54fb9e196a44b3186283e29018a9c2331f51763921e18e891b3c60c283615a27540ec8eb4c8b89f41c237b91f732a7aa518b2eb7a0d434d + languageName: node + linkType: hard + +"@babel/types@npm:^7.21.3, @babel/types@npm:^7.24.7, @babel/types@npm:^7.24.8, @babel/types@npm:^7.24.9, @babel/types@npm:^7.4.4, @babel/types@npm:^7.8.3": + version: 7.24.9 + resolution: "@babel/types@npm:7.24.9" + dependencies: + "@babel/helper-string-parser": "npm:^7.24.8" + "@babel/helper-validator-identifier": "npm:^7.24.7" + to-fast-properties: "npm:^2.0.0" + checksum: 10c0/4970b3481cab39c5c3fdb7c28c834df5c7049f3c7f43baeafe121bb05270ebf0da7c65b097abf314877f213baa591109c82204f30d66cdd46c22ece4a2f32415 + languageName: node + linkType: hard + +"@biomejs/biome@npm:^1.8.3": + version: 1.8.3 + resolution: "@biomejs/biome@npm:1.8.3" + dependencies: + "@biomejs/cli-darwin-arm64": "npm:1.8.3" + "@biomejs/cli-darwin-x64": "npm:1.8.3" + "@biomejs/cli-linux-arm64": "npm:1.8.3" + "@biomejs/cli-linux-arm64-musl": "npm:1.8.3" + "@biomejs/cli-linux-x64": "npm:1.8.3" + "@biomejs/cli-linux-x64-musl": "npm:1.8.3" + "@biomejs/cli-win32-arm64": "npm:1.8.3" + "@biomejs/cli-win32-x64": "npm:1.8.3" + dependenciesMeta: + "@biomejs/cli-darwin-arm64": + optional: true + "@biomejs/cli-darwin-x64": + optional: true + "@biomejs/cli-linux-arm64": + optional: true + "@biomejs/cli-linux-arm64-musl": + optional: true + "@biomejs/cli-linux-x64": + optional: true + "@biomejs/cli-linux-x64-musl": + optional: true + "@biomejs/cli-win32-arm64": + optional: true + "@biomejs/cli-win32-x64": + optional: true + bin: + biome: bin/biome + checksum: 10c0/95fe99ce82cd8242f1be51cbf3ac26043b253f5a369d3dc24df09bdb32ec04dba679b1d4fa8b9d602b1bf2c30ecd80af14aa8f5c92d6e0cd6214a99a1099a65b + languageName: node + linkType: hard + +"@biomejs/cli-darwin-arm64@npm:1.8.3": + version: 1.8.3 + resolution: "@biomejs/cli-darwin-arm64@npm:1.8.3" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@biomejs/cli-darwin-x64@npm:1.8.3": + version: 1.8.3 + resolution: "@biomejs/cli-darwin-x64@npm:1.8.3" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@biomejs/cli-linux-arm64-musl@npm:1.8.3": + version: 1.8.3 + resolution: "@biomejs/cli-linux-arm64-musl@npm:1.8.3" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@biomejs/cli-linux-arm64@npm:1.8.3": + version: 1.8.3 + resolution: "@biomejs/cli-linux-arm64@npm:1.8.3" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@biomejs/cli-linux-x64-musl@npm:1.8.3": + version: 1.8.3 + resolution: "@biomejs/cli-linux-x64-musl@npm:1.8.3" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@biomejs/cli-linux-x64@npm:1.8.3": + version: 1.8.3 + resolution: "@biomejs/cli-linux-x64@npm:1.8.3" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@biomejs/cli-win32-arm64@npm:1.8.3": + version: 1.8.3 + resolution: "@biomejs/cli-win32-arm64@npm:1.8.3" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@biomejs/cli-win32-x64@npm:1.8.3": + version: 1.8.3 + resolution: "@biomejs/cli-win32-x64@npm:1.8.3" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@cspotcode/source-map-support@npm:^0.8.0": + version: 0.8.1 + resolution: "@cspotcode/source-map-support@npm:0.8.1" + dependencies: + "@jridgewell/trace-mapping": "npm:0.3.9" + checksum: 10c0/05c5368c13b662ee4c122c7bfbe5dc0b613416672a829f3e78bc49a357a197e0218d6e74e7c66cfcd04e15a179acab080bd3c69658c9fbefd0e1ccd950a07fc6 + languageName: node + linkType: hard + +"@emnapi/core@npm:^1.1.0": + version: 1.2.0 + resolution: "@emnapi/core@npm:1.2.0" + dependencies: + "@emnapi/wasi-threads": "npm:1.0.1" + tslib: "npm:^2.4.0" + checksum: 10c0/a9cf024c1982cd965f6888d1b4514926ad3675fa9d0bd792c9a0770fb592c4c4d20aa1e97a225a7682f9c7900231751434820d5558fd5a00929c2ee976ce5265 + languageName: node + linkType: hard + +"@emnapi/runtime@npm:^1.1.0, @emnapi/runtime@npm:^1.1.1": + version: 1.2.0 + resolution: "@emnapi/runtime@npm:1.2.0" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10c0/7005ff8b67724c9e61b6cd79a3decbdb2ce25d24abd4d3d187472f200ee6e573329c30264335125fb136bd813aa9cf9f4f7c9391d04b07dd1e63ce0a3427be57 + languageName: node + linkType: hard + +"@emnapi/wasi-threads@npm:1.0.1": + version: 1.0.1 + resolution: "@emnapi/wasi-threads@npm:1.0.1" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10c0/1e0c8036b8d53e9b07cc9acf021705ef6c86ab6b13e1acda7fffaf541a2d3565072afb92597419173ced9ea14f6bf32fce149106e669b5902b825e8b499e5c6c + languageName: node + linkType: hard + +"@hookform/resolvers@npm:^3.9.0": + version: 3.9.0 + resolution: "@hookform/resolvers@npm:3.9.0" + peerDependencies: + react-hook-form: ^7.0.0 + checksum: 10c0/0e0e55f63abbd212cf14abbd39afad1f9b6105d6b25ce827fc651b624ed2be467ebe9b186026e0f032062db59ce2370b14e9583b436ae2d057738bdd6f04356c + languageName: node + linkType: hard + +"@hutson/parse-repository-url@npm:^3.0.0": + version: 3.0.2 + resolution: "@hutson/parse-repository-url@npm:3.0.2" + checksum: 10c0/d9197757ecad2df18d29d3e1d1fe0716d458fd88b849c71cbec9e78239f911074c97e8d764dfd8ed890431c1137e52dd7a337207fd65be20ce0784f7860ae4d1 + languageName: node + linkType: hard + +"@img/sharp-darwin-arm64@npm:0.33.4": + version: 0.33.4 + resolution: "@img/sharp-darwin-arm64@npm:0.33.4" + dependencies: + "@img/sharp-libvips-darwin-arm64": "npm:1.0.2" + dependenciesMeta: + "@img/sharp-libvips-darwin-arm64": + optional: true + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@img/sharp-darwin-x64@npm:0.33.4": + version: 0.33.4 + resolution: "@img/sharp-darwin-x64@npm:0.33.4" + dependencies: + "@img/sharp-libvips-darwin-x64": "npm:1.0.2" + dependenciesMeta: + "@img/sharp-libvips-darwin-x64": + optional: true + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@img/sharp-libvips-darwin-arm64@npm:1.0.2": + version: 1.0.2 + resolution: "@img/sharp-libvips-darwin-arm64@npm:1.0.2" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@img/sharp-libvips-darwin-x64@npm:1.0.2": + version: 1.0.2 + resolution: "@img/sharp-libvips-darwin-x64@npm:1.0.2" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-arm64@npm:1.0.2": + version: 1.0.2 + resolution: "@img/sharp-libvips-linux-arm64@npm:1.0.2" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-arm@npm:1.0.2": + version: 1.0.2 + resolution: "@img/sharp-libvips-linux-arm@npm:1.0.2" + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-s390x@npm:1.0.2": + version: 1.0.2 + resolution: "@img/sharp-libvips-linux-s390x@npm:1.0.2" + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linux-x64@npm:1.0.2": + version: 1.0.2 + resolution: "@img/sharp-libvips-linux-x64@npm:1.0.2" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-libvips-linuxmusl-arm64@npm:1.0.2": + version: 1.0.2 + resolution: "@img/sharp-libvips-linuxmusl-arm64@npm:1.0.2" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-libvips-linuxmusl-x64@npm:1.0.2": + version: 1.0.2 + resolution: "@img/sharp-libvips-linuxmusl-x64@npm:1.0.2" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-linux-arm64@npm:0.33.4": + version: 0.33.4 + resolution: "@img/sharp-linux-arm64@npm:0.33.4" + dependencies: + "@img/sharp-libvips-linux-arm64": "npm:1.0.2" + dependenciesMeta: + "@img/sharp-libvips-linux-arm64": + optional: true + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-arm@npm:0.33.4": + version: 0.33.4 + resolution: "@img/sharp-linux-arm@npm:0.33.4" + dependencies: + "@img/sharp-libvips-linux-arm": "npm:1.0.2" + dependenciesMeta: + "@img/sharp-libvips-linux-arm": + optional: true + conditions: os=linux & cpu=arm & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-s390x@npm:0.33.4": + version: 0.33.4 + resolution: "@img/sharp-linux-s390x@npm:0.33.4" + dependencies: + "@img/sharp-libvips-linux-s390x": "npm:1.0.2" + dependenciesMeta: + "@img/sharp-libvips-linux-s390x": + optional: true + conditions: os=linux & cpu=s390x & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linux-x64@npm:0.33.4": + version: 0.33.4 + resolution: "@img/sharp-linux-x64@npm:0.33.4" + dependencies: + "@img/sharp-libvips-linux-x64": "npm:1.0.2" + dependenciesMeta: + "@img/sharp-libvips-linux-x64": + optional: true + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@img/sharp-linuxmusl-arm64@npm:0.33.4": + version: 0.33.4 + resolution: "@img/sharp-linuxmusl-arm64@npm:0.33.4" + dependencies: + "@img/sharp-libvips-linuxmusl-arm64": "npm:1.0.2" + dependenciesMeta: + "@img/sharp-libvips-linuxmusl-arm64": + optional: true + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-linuxmusl-x64@npm:0.33.4": + version: 0.33.4 + resolution: "@img/sharp-linuxmusl-x64@npm:0.33.4" + dependencies: + "@img/sharp-libvips-linuxmusl-x64": "npm:1.0.2" + dependenciesMeta: + "@img/sharp-libvips-linuxmusl-x64": + optional: true + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@img/sharp-wasm32@npm:0.33.4": + version: 0.33.4 + resolution: "@img/sharp-wasm32@npm:0.33.4" + dependencies: + "@emnapi/runtime": "npm:^1.1.1" + conditions: cpu=wasm32 + languageName: node + linkType: hard + +"@img/sharp-win32-ia32@npm:0.33.4": + version: 0.33.4 + resolution: "@img/sharp-win32-ia32@npm:0.33.4" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@img/sharp-win32-x64@npm:0.33.4": + version: 0.33.4 + resolution: "@img/sharp-win32-x64@npm:0.33.4" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@ioredis/commands@npm:^1.1.1": + version: 1.2.0 + resolution: "@ioredis/commands@npm:1.2.0" + checksum: 10c0/a5d3c29dd84d8a28b7c67a441ac1715cbd7337a7b88649c0f17c345d89aa218578d2b360760017c48149ef8a70f44b051af9ac0921a0622c2b479614c4f65b36 + languageName: node + linkType: hard + +"@isaacs/cliui@npm:^8.0.2": + version: 8.0.2 + resolution: "@isaacs/cliui@npm:8.0.2" + dependencies: + string-width: "npm:^5.1.2" + string-width-cjs: "npm:string-width@^4.2.0" + strip-ansi: "npm:^7.0.1" + strip-ansi-cjs: "npm:strip-ansi@^6.0.1" + wrap-ansi: "npm:^8.1.0" + wrap-ansi-cjs: "npm:wrap-ansi@^7.0.0" + checksum: 10c0/b1bf42535d49f11dc137f18d5e4e63a28c5569de438a221c369483731e9dac9fb797af554e8bf02b6192d1e5eba6e6402cf93900c3d0ac86391d00d04876789e + languageName: node + linkType: hard + +"@isaacs/string-locale-compare@npm:^1.1.0": + version: 1.1.0 + resolution: "@isaacs/string-locale-compare@npm:1.1.0" + checksum: 10c0/d67226ff7ac544a495c77df38187e69e0e3a0783724777f86caadafb306e2155dc3b5787d5927916ddd7fb4a53561ac8f705448ac3235d18ea60da5854829fdf + languageName: node + linkType: hard + +"@jest/schemas@npm:^29.6.3": + version: 29.6.3 + resolution: "@jest/schemas@npm:29.6.3" + dependencies: + "@sinclair/typebox": "npm:^0.27.8" + checksum: 10c0/b329e89cd5f20b9278ae1233df74016ebf7b385e0d14b9f4c1ad18d096c4c19d1e687aa113a9c976b16ec07f021ae53dea811fb8c1248a50ac34fbe009fdf6be + languageName: node + linkType: hard + +"@jridgewell/gen-mapping@npm:^0.3.2, @jridgewell/gen-mapping@npm:^0.3.5": + version: 0.3.5 + resolution: "@jridgewell/gen-mapping@npm:0.3.5" + dependencies: + "@jridgewell/set-array": "npm:^1.2.1" + "@jridgewell/sourcemap-codec": "npm:^1.4.10" + "@jridgewell/trace-mapping": "npm:^0.3.24" + checksum: 10c0/1be4fd4a6b0f41337c4f5fdf4afc3bd19e39c3691924817108b82ffcb9c9e609c273f936932b9fba4b3a298ce2eb06d9bff4eb1cc3bd81c4f4ee1b4917e25feb + languageName: node + linkType: hard + +"@jridgewell/resolve-uri@npm:^3.0.3, @jridgewell/resolve-uri@npm:^3.1.0": + version: 3.1.2 + resolution: "@jridgewell/resolve-uri@npm:3.1.2" + checksum: 10c0/d502e6fb516b35032331406d4e962c21fe77cdf1cbdb49c6142bcbd9e30507094b18972778a6e27cbad756209cfe34b1a27729e6fa08a2eb92b33943f680cf1e + languageName: node + linkType: hard + +"@jridgewell/set-array@npm:^1.2.1": + version: 1.2.1 + resolution: "@jridgewell/set-array@npm:1.2.1" + checksum: 10c0/2a5aa7b4b5c3464c895c802d8ae3f3d2b92fcbe84ad12f8d0bfbb1f5ad006717e7577ee1fd2eac00c088abe486c7adb27976f45d2941ff6b0b92b2c3302c60f4 + languageName: node + linkType: hard + +"@jridgewell/sourcemap-codec@npm:^1.4.10, @jridgewell/sourcemap-codec@npm:^1.4.14": + version: 1.5.0 + resolution: "@jridgewell/sourcemap-codec@npm:1.5.0" + checksum: 10c0/2eb864f276eb1096c3c11da3e9bb518f6d9fc0023c78344cdc037abadc725172c70314bdb360f2d4b7bffec7f5d657ce006816bc5d4ecb35e61b66132db00c18 + languageName: node + linkType: hard + +"@jridgewell/trace-mapping@npm:0.3.9": + version: 0.3.9 + resolution: "@jridgewell/trace-mapping@npm:0.3.9" + dependencies: + "@jridgewell/resolve-uri": "npm:^3.0.3" + "@jridgewell/sourcemap-codec": "npm:^1.4.10" + checksum: 10c0/fa425b606d7c7ee5bfa6a31a7b050dd5814b4082f318e0e4190f991902181b4330f43f4805db1dd4f2433fd0ed9cc7a7b9c2683f1deeab1df1b0a98b1e24055b + languageName: node + linkType: hard + +"@jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25": + version: 0.3.25 + resolution: "@jridgewell/trace-mapping@npm:0.3.25" + dependencies: + "@jridgewell/resolve-uri": "npm:^3.1.0" + "@jridgewell/sourcemap-codec": "npm:^1.4.14" + checksum: 10c0/3d1ce6ebc69df9682a5a8896b414c6537e428a1d68b02fcc8363b04284a8ca0df04d0ee3013132252ab14f2527bc13bea6526a912ecb5658f0e39fd2860b4df4 + languageName: node + linkType: hard + +"@lerna/create@npm:8.1.6": + version: 8.1.6 + resolution: "@lerna/create@npm:8.1.6" + dependencies: + "@npmcli/arborist": "npm:7.5.3" + "@npmcli/package-json": "npm:5.2.0" + "@npmcli/run-script": "npm:8.1.0" + "@nx/devkit": "npm:>=17.1.2 < 20" + "@octokit/plugin-enterprise-rest": "npm:6.0.1" + "@octokit/rest": "npm:19.0.11" + aproba: "npm:2.0.0" + byte-size: "npm:8.1.1" + chalk: "npm:4.1.0" + clone-deep: "npm:4.0.1" + cmd-shim: "npm:6.0.3" + color-support: "npm:1.1.3" + columnify: "npm:1.6.0" + console-control-strings: "npm:^1.1.0" + conventional-changelog-core: "npm:5.0.1" + conventional-recommended-bump: "npm:7.0.1" + cosmiconfig: "npm:^8.2.0" + dedent: "npm:1.5.3" + execa: "npm:5.0.0" + fs-extra: "npm:^11.2.0" + get-stream: "npm:6.0.0" + git-url-parse: "npm:14.0.0" + glob-parent: "npm:6.0.2" + globby: "npm:11.1.0" + graceful-fs: "npm:4.2.11" + has-unicode: "npm:2.0.1" + ini: "npm:^1.3.8" + init-package-json: "npm:6.0.3" + inquirer: "npm:^8.2.4" + is-ci: "npm:3.0.1" + is-stream: "npm:2.0.0" + js-yaml: "npm:4.1.0" + libnpmpublish: "npm:9.0.9" + load-json-file: "npm:6.2.0" + lodash: "npm:^4.17.21" + make-dir: "npm:4.0.0" + minimatch: "npm:3.0.5" + multimatch: "npm:5.0.0" + node-fetch: "npm:2.6.7" + npm-package-arg: "npm:11.0.2" + npm-packlist: "npm:8.0.2" + npm-registry-fetch: "npm:^17.1.0" + nx: "npm:>=17.1.2 < 20" + p-map: "npm:4.0.0" + p-map-series: "npm:2.1.0" + p-queue: "npm:6.6.2" + p-reduce: "npm:^2.1.0" + pacote: "npm:^18.0.6" + pify: "npm:5.0.0" + read-cmd-shim: "npm:4.0.0" + resolve-from: "npm:5.0.0" + rimraf: "npm:^4.4.1" + semver: "npm:^7.3.4" + set-blocking: "npm:^2.0.0" + signal-exit: "npm:3.0.7" + slash: "npm:^3.0.0" + ssri: "npm:^10.0.6" + string-width: "npm:^4.2.3" + strong-log-transformer: "npm:2.1.0" + tar: "npm:6.2.1" + temp-dir: "npm:1.0.0" + upath: "npm:2.0.1" + uuid: "npm:^10.0.0" + validate-npm-package-license: "npm:^3.0.4" + validate-npm-package-name: "npm:5.0.1" + wide-align: "npm:1.1.5" + write-file-atomic: "npm:5.0.1" + write-pkg: "npm:4.0.0" + yargs: "npm:17.7.2" + yargs-parser: "npm:21.1.1" + checksum: 10c0/833920656c3254444db7d7ea3e22331773c87c635d899ef3cd785a599fe07e2a40ceec49ad46e1962d354d96877de341615ef49e12b50ed70011df6d9dda46e6 + languageName: node + linkType: hard + +"@mapbox/node-pre-gyp@npm:^1.0.11": + version: 1.0.11 + resolution: "@mapbox/node-pre-gyp@npm:1.0.11" + dependencies: + detect-libc: "npm:^2.0.0" + https-proxy-agent: "npm:^5.0.0" + make-dir: "npm:^3.1.0" + node-fetch: "npm:^2.6.7" + nopt: "npm:^5.0.0" + npmlog: "npm:^5.0.1" + rimraf: "npm:^3.0.2" + semver: "npm:^7.3.5" + tar: "npm:^6.1.11" + bin: + node-pre-gyp: bin/node-pre-gyp + checksum: 10c0/2b24b93c31beca1c91336fa3b3769fda98e202fb7f9771f0f4062588d36dcc30fcf8118c36aa747fa7f7610d8cf601872bdaaf62ce7822bb08b545d1bbe086cc + languageName: node + linkType: hard + +"@monaco-editor/loader@npm:^1.4.0": + version: 1.4.0 + resolution: "@monaco-editor/loader@npm:1.4.0" + dependencies: + state-local: "npm:^1.0.6" + peerDependencies: + monaco-editor: ">= 0.21.0 < 1" + checksum: 10c0/68938350adf2f42363a801d87f5d00c87d397d4cba7041141af10a9216bd35c85209b4723a26d56cb32e68eef61471deda2a450f8892891118fbdce7fa1d987d + languageName: node + linkType: hard + +"@monaco-editor/react@npm:^4.6.0": + version: 4.6.0 + resolution: "@monaco-editor/react@npm:4.6.0" + dependencies: + "@monaco-editor/loader": "npm:^1.4.0" + peerDependencies: + monaco-editor: ">= 0.25.0 < 1" + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 10c0/231e9a9b66a530db326f6732de0ebffcce6b79dcfaf4948923d78b9a3d5e2a04b7a06e1f85bbbca45a5ae15c107a124e4c5c46cabadc20a498fb5f2d05f7f379 + languageName: node + linkType: hard + +"@napi-rs/wasm-runtime@npm:0.2.4": + version: 0.2.4 + resolution: "@napi-rs/wasm-runtime@npm:0.2.4" + dependencies: + "@emnapi/core": "npm:^1.1.0" + "@emnapi/runtime": "npm:^1.1.0" + "@tybys/wasm-util": "npm:^0.9.0" + checksum: 10c0/1040de49b2ef509db207e2517465dbf7fb3474f20e8ec32897672a962ff4f59872385666dac61dc9dbeae3cae5dad265d8dc3865da756adeb07d1634c67b03a1 + languageName: node + linkType: hard + +"@next/env@npm:14.2.5": + version: 14.2.5 + resolution: "@next/env@npm:14.2.5" + checksum: 10c0/63d8b88ac450b3c37940a9e2119a63a1074aca89908574ade6157a8aa295275dcb3ac5f69e00883fc55d0f12963b73b74e87ba32a5768a489f9609c6be57b699 + languageName: node + linkType: hard + +"@next/swc-darwin-arm64@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-darwin-arm64@npm:14.2.5" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@next/swc-darwin-x64@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-darwin-x64@npm:14.2.5" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@next/swc-linux-arm64-gnu@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-linux-arm64-gnu@npm:14.2.5" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@next/swc-linux-arm64-musl@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-linux-arm64-musl@npm:14.2.5" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@next/swc-linux-x64-gnu@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-linux-x64-gnu@npm:14.2.5" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@next/swc-linux-x64-musl@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-linux-x64-musl@npm:14.2.5" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@next/swc-win32-arm64-msvc@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-win32-arm64-msvc@npm:14.2.5" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@next/swc-win32-ia32-msvc@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-win32-ia32-msvc@npm:14.2.5" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@next/swc-win32-x64-msvc@npm:14.2.5": + version: 14.2.5 + resolution: "@next/swc-win32-x64-msvc@npm:14.2.5" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@nodelib/fs.scandir@npm:2.1.5": + version: 2.1.5 + resolution: "@nodelib/fs.scandir@npm:2.1.5" + dependencies: + "@nodelib/fs.stat": "npm:2.0.5" + run-parallel: "npm:^1.1.9" + checksum: 10c0/732c3b6d1b1e967440e65f284bd06e5821fedf10a1bea9ed2bb75956ea1f30e08c44d3def9d6a230666574edbaf136f8cfd319c14fd1f87c66e6a44449afb2eb + languageName: node + linkType: hard + +"@nodelib/fs.stat@npm:2.0.5, @nodelib/fs.stat@npm:^2.0.2": + version: 2.0.5 + resolution: "@nodelib/fs.stat@npm:2.0.5" + checksum: 10c0/88dafe5e3e29a388b07264680dc996c17f4bda48d163a9d4f5c1112979f0ce8ec72aa7116122c350b4e7976bc5566dc3ddb579be1ceaacc727872eb4ed93926d + languageName: node + linkType: hard + +"@nodelib/fs.walk@npm:^1.2.3": + version: 1.2.8 + resolution: "@nodelib/fs.walk@npm:1.2.8" + dependencies: + "@nodelib/fs.scandir": "npm:2.1.5" + fastq: "npm:^1.6.0" + checksum: 10c0/db9de047c3bb9b51f9335a7bb46f4fcfb6829fb628318c12115fbaf7d369bfce71c15b103d1fc3b464812d936220ee9bc1c8f762d032c9f6be9acc99249095b1 + languageName: node + linkType: hard + +"@npmcli/agent@npm:^2.0.0": + version: 2.2.2 + resolution: "@npmcli/agent@npm:2.2.2" + dependencies: + agent-base: "npm:^7.1.0" + http-proxy-agent: "npm:^7.0.0" + https-proxy-agent: "npm:^7.0.1" + lru-cache: "npm:^10.0.1" + socks-proxy-agent: "npm:^8.0.3" + checksum: 10c0/325e0db7b287d4154ecd164c0815c08007abfb07653cc57bceded17bb7fd240998a3cbdbe87d700e30bef494885eccc725ab73b668020811d56623d145b524ae + languageName: node + linkType: hard + +"@npmcli/arborist@npm:7.5.3": + version: 7.5.3 + resolution: "@npmcli/arborist@npm:7.5.3" + dependencies: + "@isaacs/string-locale-compare": "npm:^1.1.0" + "@npmcli/fs": "npm:^3.1.1" + "@npmcli/installed-package-contents": "npm:^2.1.0" + "@npmcli/map-workspaces": "npm:^3.0.2" + "@npmcli/metavuln-calculator": "npm:^7.1.1" + "@npmcli/name-from-folder": "npm:^2.0.0" + "@npmcli/node-gyp": "npm:^3.0.0" + "@npmcli/package-json": "npm:^5.1.0" + "@npmcli/query": "npm:^3.1.0" + "@npmcli/redact": "npm:^2.0.0" + "@npmcli/run-script": "npm:^8.1.0" + bin-links: "npm:^4.0.4" + cacache: "npm:^18.0.3" + common-ancestor-path: "npm:^1.0.1" + hosted-git-info: "npm:^7.0.2" + json-parse-even-better-errors: "npm:^3.0.2" + json-stringify-nice: "npm:^1.1.4" + lru-cache: "npm:^10.2.2" + minimatch: "npm:^9.0.4" + nopt: "npm:^7.2.1" + npm-install-checks: "npm:^6.2.0" + npm-package-arg: "npm:^11.0.2" + npm-pick-manifest: "npm:^9.0.1" + npm-registry-fetch: "npm:^17.0.1" + pacote: "npm:^18.0.6" + parse-conflict-json: "npm:^3.0.0" + proc-log: "npm:^4.2.0" + proggy: "npm:^2.0.0" + promise-all-reject-late: "npm:^1.0.0" + promise-call-limit: "npm:^3.0.1" + read-package-json-fast: "npm:^3.0.2" + semver: "npm:^7.3.7" + ssri: "npm:^10.0.6" + treeverse: "npm:^3.0.0" + walk-up-path: "npm:^3.0.1" + bin: + arborist: bin/index.js + checksum: 10c0/61e8f73f687c5c62704de6d2a081490afe6ba5e5526b9b2da44c6cb137df30256d5650235d4ece73454ddc4c40a291e26881bbcaa83c03404177cb3e05e26721 + languageName: node + linkType: hard + +"@npmcli/fs@npm:^3.1.0, @npmcli/fs@npm:^3.1.1": + version: 3.1.1 + resolution: "@npmcli/fs@npm:3.1.1" + dependencies: + semver: "npm:^7.3.5" + checksum: 10c0/c37a5b4842bfdece3d14dfdb054f73fe15ed2d3da61b34ff76629fb5b1731647c49166fd2a8bf8b56fcfa51200382385ea8909a3cbecdad612310c114d3f6c99 + languageName: node + linkType: hard + +"@npmcli/git@npm:^5.0.0": + version: 5.0.8 + resolution: "@npmcli/git@npm:5.0.8" + dependencies: + "@npmcli/promise-spawn": "npm:^7.0.0" + ini: "npm:^4.1.3" + lru-cache: "npm:^10.0.1" + npm-pick-manifest: "npm:^9.0.0" + proc-log: "npm:^4.0.0" + promise-inflight: "npm:^1.0.1" + promise-retry: "npm:^2.0.1" + semver: "npm:^7.3.5" + which: "npm:^4.0.0" + checksum: 10c0/892441c968404950809c7b515a93b78167ea1db2252f259f390feae22a2c5477f3e1629e105e19a084c05afc56e585bf3f13c2f13b54a06bfd6786f0c8429532 + languageName: node + linkType: hard + +"@npmcli/installed-package-contents@npm:^2.0.1, @npmcli/installed-package-contents@npm:^2.1.0": + version: 2.1.0 + resolution: "@npmcli/installed-package-contents@npm:2.1.0" + dependencies: + npm-bundled: "npm:^3.0.0" + npm-normalize-package-bin: "npm:^3.0.0" + bin: + installed-package-contents: bin/index.js + checksum: 10c0/f5ecba0d45fc762f3e0d5def29fbfabd5d55e8147b01ae0a101769245c2e0038bc82a167836513a98aaed0a15c3d81fcdb232056bb8a962972a432533e518fce + languageName: node + linkType: hard + +"@npmcli/map-workspaces@npm:^3.0.2": + version: 3.0.6 + resolution: "@npmcli/map-workspaces@npm:3.0.6" + dependencies: + "@npmcli/name-from-folder": "npm:^2.0.0" + glob: "npm:^10.2.2" + minimatch: "npm:^9.0.0" + read-package-json-fast: "npm:^3.0.0" + checksum: 10c0/6bfcf8ca05ab9ddc2bd19c0fd91e9982f03cc6e67b0c03f04ba4d2f29b7d83f96e759c0f8f1f4b6dbe3182272483643a0d1269788352edd0c883d6fbfa2f3f14 + languageName: node + linkType: hard + +"@npmcli/metavuln-calculator@npm:^7.1.1": + version: 7.1.1 + resolution: "@npmcli/metavuln-calculator@npm:7.1.1" + dependencies: + cacache: "npm:^18.0.0" + json-parse-even-better-errors: "npm:^3.0.0" + pacote: "npm:^18.0.0" + proc-log: "npm:^4.1.0" + semver: "npm:^7.3.5" + checksum: 10c0/27402cab124bb1fca56af7549f730c38c0ab40de60cbef6264a4193c26c2d28cefb2adac29ed27f368031795704f9f8fe0c547c4c8cb0c0fa94d72330d56ac80 + languageName: node + linkType: hard + +"@npmcli/name-from-folder@npm:^2.0.0": + version: 2.0.0 + resolution: "@npmcli/name-from-folder@npm:2.0.0" + checksum: 10c0/1aa551771d98ab366d4cb06b33efd3bb62b609942f6d9c3bb667c10e5bb39a223d3e330022bc980a44402133e702ae67603862099ac8254dad11f90e77409827 + languageName: node + linkType: hard + +"@npmcli/node-gyp@npm:^3.0.0": + version: 3.0.0 + resolution: "@npmcli/node-gyp@npm:3.0.0" + checksum: 10c0/5d0ac17dacf2dd6e45312af2c1ae2749bb0730fcc82da101c37d3a4fd963a5e1c5d39781e5e1e5e5828df4ab1ad4e3fdbab1d69b7cd0abebad9983efb87df985 + languageName: node + linkType: hard + +"@npmcli/package-json@npm:5.2.0, @npmcli/package-json@npm:^5.0.0, @npmcli/package-json@npm:^5.1.0": + version: 5.2.0 + resolution: "@npmcli/package-json@npm:5.2.0" + dependencies: + "@npmcli/git": "npm:^5.0.0" + glob: "npm:^10.2.2" + hosted-git-info: "npm:^7.0.0" + json-parse-even-better-errors: "npm:^3.0.0" + normalize-package-data: "npm:^6.0.0" + proc-log: "npm:^4.0.0" + semver: "npm:^7.5.3" + checksum: 10c0/bdce8c7eed0dee1d272bf8ba500c4bce6d8ed2b4dd2ce43075d3ba02ffd3bb70c46dbcf8b3a35e19d9492d039b720dc3a4b30d1a2ddc30b7918e1d5232faa1f7 + languageName: node + linkType: hard + +"@npmcli/promise-spawn@npm:^7.0.0": + version: 7.0.2 + resolution: "@npmcli/promise-spawn@npm:7.0.2" + dependencies: + which: "npm:^4.0.0" + checksum: 10c0/8f2af5bc2c1b1ccfb9bcd91da8873ab4723616d8bd5af877c0daa40b1e2cbfa4afb79e052611284179cae918c945a1b99ae1c565d78a355bec1a461011e89f71 + languageName: node + linkType: hard + +"@npmcli/query@npm:^3.1.0": + version: 3.1.0 + resolution: "@npmcli/query@npm:3.1.0" + dependencies: + postcss-selector-parser: "npm:^6.0.10" + checksum: 10c0/9a099677dd188a2d9eb7a49e32c69d315b09faea59e851b7c2013b5bda915a38434efa7295565c40a1098916c06ebfa1840f68d831180e36842f48c24f4c5186 + languageName: node + linkType: hard + +"@npmcli/redact@npm:^2.0.0": + version: 2.0.1 + resolution: "@npmcli/redact@npm:2.0.1" + checksum: 10c0/5f346f7ef224b44c90009939f93c446a865a3d9e5a7ebe0246cdb0ebd03219de3962ee6c6e9197298d8c6127ea33535e8c44814276e4941394dc1cdf1f30f6bc + languageName: node + linkType: hard + +"@npmcli/run-script@npm:8.1.0, @npmcli/run-script@npm:^8.0.0, @npmcli/run-script@npm:^8.1.0": + version: 8.1.0 + resolution: "@npmcli/run-script@npm:8.1.0" + dependencies: + "@npmcli/node-gyp": "npm:^3.0.0" + "@npmcli/package-json": "npm:^5.0.0" + "@npmcli/promise-spawn": "npm:^7.0.0" + node-gyp: "npm:^10.0.0" + proc-log: "npm:^4.0.0" + which: "npm:^4.0.0" + checksum: 10c0/f9f40ecff0406a9ce1b77c9f714fc7c71b561289361efc6e2e0e48ca2d630aa98d277cbbf269750f9467a40eaaac79e78766d67c458046aa9507c8c354650fee + languageName: node + linkType: hard + +"@nrwl/devkit@npm:19.5.1": + version: 19.5.1 + resolution: "@nrwl/devkit@npm:19.5.1" + dependencies: + "@nx/devkit": "npm:19.5.1" + checksum: 10c0/133760dcc1038894e396cf6eeaf834ef3553487cc3c11be2294cabe065e725d0fc4feb51db0b5dc436c8d1ff1bdd34e49698c38404af9883210012bb4878ed8c + languageName: node + linkType: hard + +"@nrwl/tao@npm:19.5.1": + version: 19.5.1 + resolution: "@nrwl/tao@npm:19.5.1" + dependencies: + nx: "npm:19.5.1" + tslib: "npm:^2.3.0" + bin: + tao: index.js + checksum: 10c0/2fe5038a8c1a206f8bb72ce448c888ed94ee31bfb79061a8ee33332c0e0077d9b84335178dec740cd4f79de4ea289485d13ce1c4005778a8dbd622e0a5f0532a + languageName: node + linkType: hard + +"@nx/devkit@npm:19.5.1, @nx/devkit@npm:>=17.1.2 < 20": + version: 19.5.1 + resolution: "@nx/devkit@npm:19.5.1" + dependencies: + "@nrwl/devkit": "npm:19.5.1" + ejs: "npm:^3.1.7" + enquirer: "npm:~2.3.6" + ignore: "npm:^5.0.4" + minimatch: "npm:9.0.3" + semver: "npm:^7.5.3" + tmp: "npm:~0.2.1" + tslib: "npm:^2.3.0" + yargs-parser: "npm:21.1.1" + peerDependencies: + nx: ">= 17 <= 20" + checksum: 10c0/ecc29aa99ad10c6b83a8d2477da3b94c6a2d73e9950ad699a9377f26b389b7cc4414fee9aec3b27a6e0dddfccf7abb462a33d924525d37fac7e4b3db38721f51 + languageName: node + linkType: hard + +"@nx/nx-darwin-arm64@npm:19.5.1": + version: 19.5.1 + resolution: "@nx/nx-darwin-arm64@npm:19.5.1" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@nx/nx-darwin-x64@npm:19.5.1": + version: 19.5.1 + resolution: "@nx/nx-darwin-x64@npm:19.5.1" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@nx/nx-freebsd-x64@npm:19.5.1": + version: 19.5.1 + resolution: "@nx/nx-freebsd-x64@npm:19.5.1" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@nx/nx-linux-arm-gnueabihf@npm:19.5.1": + version: 19.5.1 + resolution: "@nx/nx-linux-arm-gnueabihf@npm:19.5.1" + conditions: os=linux & cpu=arm + languageName: node + linkType: hard + +"@nx/nx-linux-arm64-gnu@npm:19.5.1": + version: 19.5.1 + resolution: "@nx/nx-linux-arm64-gnu@npm:19.5.1" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@nx/nx-linux-arm64-musl@npm:19.5.1": + version: 19.5.1 + resolution: "@nx/nx-linux-arm64-musl@npm:19.5.1" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@nx/nx-linux-x64-gnu@npm:19.5.1": + version: 19.5.1 + resolution: "@nx/nx-linux-x64-gnu@npm:19.5.1" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@nx/nx-linux-x64-musl@npm:19.5.1": + version: 19.5.1 + resolution: "@nx/nx-linux-x64-musl@npm:19.5.1" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@nx/nx-win32-arm64-msvc@npm:19.5.1": + version: 19.5.1 + resolution: "@nx/nx-win32-arm64-msvc@npm:19.5.1" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@nx/nx-win32-x64-msvc@npm:19.5.1": + version: 19.5.1 + resolution: "@nx/nx-win32-x64-msvc@npm:19.5.1" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@octokit/auth-token@npm:^3.0.0": + version: 3.0.4 + resolution: "@octokit/auth-token@npm:3.0.4" + checksum: 10c0/abdf5e2da36344de9727c70ba782d58004f5ae1da0f65fa9bc9216af596ef23c0e4675f386df2f6886806612558091d603564051b693b0ad1986aa6160b7a231 + languageName: node + linkType: hard + +"@octokit/core@npm:^4.2.1": + version: 4.2.4 + resolution: "@octokit/core@npm:4.2.4" + dependencies: + "@octokit/auth-token": "npm:^3.0.0" + "@octokit/graphql": "npm:^5.0.0" + "@octokit/request": "npm:^6.0.0" + "@octokit/request-error": "npm:^3.0.0" + "@octokit/types": "npm:^9.0.0" + before-after-hook: "npm:^2.2.0" + universal-user-agent: "npm:^6.0.0" + checksum: 10c0/e54081a56884e628d1804837fddcd48c10d516117bb891551c8dc9d8e3dad449aeb9b4677ca71e8f0e76268c2b7656c953099506679aaa4666765228474a3ce6 + languageName: node + linkType: hard + +"@octokit/endpoint@npm:^7.0.0": + version: 7.0.6 + resolution: "@octokit/endpoint@npm:7.0.6" + dependencies: + "@octokit/types": "npm:^9.0.0" + is-plain-object: "npm:^5.0.0" + universal-user-agent: "npm:^6.0.0" + checksum: 10c0/fd147a55010b54af7567bf90791359f7096a1c9916a2b7c72f8afd0c53141338b3d78da3a4ab3e3bdfeb26218a1b73735432d8987ccc04996b1019219299f115 + languageName: node + linkType: hard + +"@octokit/graphql@npm:^5.0.0": + version: 5.0.6 + resolution: "@octokit/graphql@npm:5.0.6" + dependencies: + "@octokit/request": "npm:^6.0.0" + "@octokit/types": "npm:^9.0.0" + universal-user-agent: "npm:^6.0.0" + checksum: 10c0/de1d839d97fe6d96179925f6714bf96e7af6f77929892596bb4211adab14add3291fc5872b269a3d0e91a4dcf248d16096c82606c4a43538cf241b815c2e2a36 + languageName: node + linkType: hard + +"@octokit/openapi-types@npm:^18.0.0": + version: 18.1.1 + resolution: "@octokit/openapi-types@npm:18.1.1" + checksum: 10c0/856d3bb9f8c666e837dd5e8b8c216ee4342b9ed63ff8da922ca4ce5883ed1dfbec73390eb13d69fbcb4703a4c8b8b6a586df3b0e675ff93bf3d46b5b4fe0968e + languageName: node + linkType: hard + +"@octokit/plugin-enterprise-rest@npm:6.0.1": + version: 6.0.1 + resolution: "@octokit/plugin-enterprise-rest@npm:6.0.1" + checksum: 10c0/26bd0a30582954efcd29b41e16698db79e9d20e3f88c4069b43b183223cee69862621f18b6a7a1c9257b1cd07c24477e403b75c74688660ecf31d467b9d8fd9e + languageName: node + linkType: hard + +"@octokit/plugin-paginate-rest@npm:^6.1.2": + version: 6.1.2 + resolution: "@octokit/plugin-paginate-rest@npm:6.1.2" + dependencies: + "@octokit/tsconfig": "npm:^1.0.2" + "@octokit/types": "npm:^9.2.3" + peerDependencies: + "@octokit/core": ">=4" + checksum: 10c0/def241c4f00b864822ab6414eaadd8679a6d332004c7e77467cfc1e6d5bdcc453c76bd185710ee942e4df201f9dd2170d960f46af5b14ef6f261a0068f656364 + languageName: node + linkType: hard + +"@octokit/plugin-request-log@npm:^1.0.4": + version: 1.0.4 + resolution: "@octokit/plugin-request-log@npm:1.0.4" + peerDependencies: + "@octokit/core": ">=3" + checksum: 10c0/7238585445555db553912e0cdef82801c89c6e5cbc62c23ae086761c23cc4a403d6c3fddd20348bbd42fb7508e2c2fce370eb18fdbe3fbae2c0d2c8be974f4cc + languageName: node + linkType: hard + +"@octokit/plugin-rest-endpoint-methods@npm:^7.1.2": + version: 7.2.3 + resolution: "@octokit/plugin-rest-endpoint-methods@npm:7.2.3" + dependencies: + "@octokit/types": "npm:^10.0.0" + peerDependencies: + "@octokit/core": ">=3" + checksum: 10c0/8bffbc5852695dd08d65cc64b6ab7d2871ed9df1e791608f48b488a3908b5b655e3686b5dd72fc37c824e82bdd4dfc9d24e2e50205bbc324667def1d705bc9da + languageName: node + linkType: hard + +"@octokit/request-error@npm:^3.0.0": + version: 3.0.3 + resolution: "@octokit/request-error@npm:3.0.3" + dependencies: + "@octokit/types": "npm:^9.0.0" + deprecation: "npm:^2.0.0" + once: "npm:^1.4.0" + checksum: 10c0/1e252ac193c8af23b709909911aa327ed5372cbafcba09e4aff41e0f640a7c152579ab0a60311a92e37b4e7936392d59ee4c2feae5cdc387ee8587a33d8afa60 + languageName: node + linkType: hard + +"@octokit/request@npm:^6.0.0": + version: 6.2.8 + resolution: "@octokit/request@npm:6.2.8" + dependencies: + "@octokit/endpoint": "npm:^7.0.0" + "@octokit/request-error": "npm:^3.0.0" + "@octokit/types": "npm:^9.0.0" + is-plain-object: "npm:^5.0.0" + node-fetch: "npm:^2.6.7" + universal-user-agent: "npm:^6.0.0" + checksum: 10c0/6b6079ed45bac44c4579b40990bfd1905b03d4bc4e5255f3d5a10cf5182171578ebe19abeab32ebb11a806f1131947f2a06b7a077bd7e77ade7b15fe2882174b + languageName: node + linkType: hard + +"@octokit/rest@npm:19.0.11": + version: 19.0.11 + resolution: "@octokit/rest@npm:19.0.11" + dependencies: + "@octokit/core": "npm:^4.2.1" + "@octokit/plugin-paginate-rest": "npm:^6.1.2" + "@octokit/plugin-request-log": "npm:^1.0.4" + "@octokit/plugin-rest-endpoint-methods": "npm:^7.1.2" + checksum: 10c0/a14ae31fc5e70e76d2492aae63d3453cbb71f44e7492400f885ab5ac6b2612bcb244bafa29e45a59461f3e5d99807ff9c88d48af8317ffa4f8ad3f8f11fdd035 + languageName: node + linkType: hard + +"@octokit/tsconfig@npm:^1.0.2": + version: 1.0.2 + resolution: "@octokit/tsconfig@npm:1.0.2" + checksum: 10c0/84db70b495beeed69259dd4def14cdfb600edeb65ef32811558c99413ee2b414ed10bff9c4dcc7a43451d0fd36b4925ada9ef7d4272b5eae38cb005cc2f459ac + languageName: node + linkType: hard + +"@octokit/types@npm:^10.0.0": + version: 10.0.0 + resolution: "@octokit/types@npm:10.0.0" + dependencies: + "@octokit/openapi-types": "npm:^18.0.0" + checksum: 10c0/9bbbec1e452c271752e5ba735c161a558933f2e35f3004bb0b6e8d6ba574af48b68bab2f293112a8e68c595435a2fbcc76f3e7333f45ba1888bb5193777a943e + languageName: node + linkType: hard + +"@octokit/types@npm:^9.0.0, @octokit/types@npm:^9.2.3": + version: 9.3.2 + resolution: "@octokit/types@npm:9.3.2" + dependencies: + "@octokit/openapi-types": "npm:^18.0.0" + checksum: 10c0/2925479aa378a4491762b4fcf381bdc7daca39b4e0b2dd7062bce5d74a32ed7d79d20d3c65ceaca6d105cf4b1f7417fea634219bf90f79a57d03e2dac629ec45 + languageName: node + linkType: hard + +"@one-ini/wasm@npm:0.1.1": + version: 0.1.1 + resolution: "@one-ini/wasm@npm:0.1.1" + checksum: 10c0/54700e055037f1a63bfcc86d24822203b25759598c2c3e295d1435130a449108aebc119c9c2e467744767dbe0b6ab47a182c61aa1071ba7368f5e20ab197ba65 + languageName: node + linkType: hard + +"@overnightjs/core@npm:^1.7.6": + version: 1.7.6 + resolution: "@overnightjs/core@npm:1.7.6" + dependencies: + express: "npm:^4.16.3" + reflect-metadata: "npm:^0.1.13" + tslib: "npm:^2.0.0" + checksum: 10c0/76c94a48a786554d5dc42c5ab116ae2fb46384533f825302e77b7d7c24e7c711cd6c35f221c6316e3c4b8700aa9760cb688eb7d3f540b2e0beb31a0ab8b185bb + languageName: node + linkType: hard + +"@pkgjs/parseargs@npm:^0.11.0": + version: 0.11.0 + resolution: "@pkgjs/parseargs@npm:0.11.0" + checksum: 10c0/5bd7576bb1b38a47a7fc7b51ac9f38748e772beebc56200450c4a817d712232b8f1d3ef70532c80840243c657d491cf6a6be1e3a214cff907645819fdc34aadd + languageName: node + linkType: hard + +"@plunk/api@workspace:packages/api": + version: 0.0.0-use.local + resolution: "@plunk/api@workspace:packages/api" + dependencies: + "@aws-sdk/client-cloudfront": "npm:^3.616.0" + "@aws-sdk/client-s3": "npm:^3.616.0" + "@aws-sdk/client-ses": "npm:^3.616.0" + "@overnightjs/core": "npm:^1.7.6" + "@plunk/shared": "npm:^1.0.0" + "@prisma/client": "npm:^5.17.0" + "@types/bcrypt": "npm:^5.0.2" + "@types/compression": "npm:^1.7.5" + "@types/cookie-parser": "npm:^1.4.7" + "@types/cors": "npm:^2.8.17" + "@types/express": "npm:^4.17.21" + "@types/ioredis": "npm:^5.0.0" + "@types/jsonwebtoken": "npm:^9.0.6" + "@types/mjml": "npm:^4.7.4" + "@types/morgan": "npm:^1.9.9" + "@types/multer": "npm:^1.4.11" + "@types/node-cron": "npm:^3.0.11" + "@types/signale": "npm:^1.4.7" + bcrypt: "npm:^5.1.1" + body-parser: "npm:^1.20.2" + compression: "npm:^1.7.4" + cookie-parser: "npm:^1.4.6" + cors: "npm:^2.8.5" + cross-env: "npm:^7.0.3" + dotenv: "npm:^16.4.5" + express: "npm:^4.19.2" + express-async-errors: "npm:^3.1.1" + helmet: "npm:^7.1.0" + ioredis: "npm:^5.4.1" + jsonwebtoken: "npm:^9.0.2" + mjml: "npm:^4.15.3" + morgan: "npm:^1.10.0" + multer: "npm:^1.4.5-lts.1" + node-cron: "npm:^3.0.3" + prisma: "npm:^5.17.0" + signale: "npm:^1.4.0" + ts-node-dev: "npm:^2.0.0" + typescript: "npm:^5.5.3" + languageName: unknown + linkType: soft + +"@plunk/dashboard@workspace:packages/dashboard": + version: 0.0.0-use.local + resolution: "@plunk/dashboard@workspace:packages/dashboard" + dependencies: + "@hookform/resolvers": "npm:^3.9.0" + "@monaco-editor/react": "npm:^4.6.0" + "@plunk/shared": "npm:1.0.0" + "@svgr/webpack": "npm:^8.1.0" + "@tailwindcss/aspect-ratio": "npm:^0.4.2" + "@tailwindcss/forms": "npm:^0.5.7" + "@tailwindcss/typography": "npm:^0.5.13" + "@tippyjs/react": "npm:^4.2.6" + "@tiptap/core": "npm:^2.5.4" + "@tiptap/extension-color": "npm:^2.5.4" + "@tiptap/extension-dropcursor": "npm:^2.5.4" + "@tiptap/extension-font-family": "npm:^2.5.4" + "@tiptap/extension-image": "npm:^2.5.4" + "@tiptap/extension-link": "npm:^2.5.4" + "@tiptap/extension-placeholder": "npm:^2.5.4" + "@tiptap/extension-text-align": "npm:^2.5.4" + "@tiptap/extension-text-style": "npm:^2.5.4" + "@tiptap/extension-typography": "npm:^2.5.4" + "@tiptap/pm": "npm:^2.5.4" + "@tiptap/react": "npm:^2.5.4" + "@tiptap/starter-kit": "npm:^2.5.4" + "@tiptap/suggestion": "npm:^2.5.4" + "@types/node": "npm:20.14.11" + "@types/nprogress": "npm:^0.2.3" + "@types/react": "npm:18.3.3" + "@types/react-syntax-highlighter": "npm:^15.5.13" + "@uiball/loaders": "npm:^1.3.1" + autoprefixer: "npm:^10.4.19" + classnames: "npm:^2.5.1" + dayjs: "npm:^1.11.12" + framer-motion: "npm:^11.3.7" + jotai: "npm:2.9.0" + lucide-react: "npm:^0.408.0" + next: "npm:14.2.5" + next-seo: "npm:^6.5.0" + nprogress: "npm:^0.2.0" + postcss: "npm:^8.4.39" + prosemirror-commands: "npm:^1.5.2" + prosemirror-dropcursor: "npm:^1.8.1" + prosemirror-gapcursor: "npm:^1.3.2" + prosemirror-history: "npm:^1.4.1" + prosemirror-keymap: "npm:^1.2.2" + prosemirror-schema-list: "npm:^1.4.1" + react: "npm:18.3.1" + react-dom: "npm:18.3.1" + react-hook-form: "npm:^7.52.1" + react-syntax-highlighter: "npm:^15.5.0" + recharts: "npm:^2.12.7" + sharp: "npm:^0.33.4" + sonner: "npm:^1.5.0" + swr: "npm:2.2.5" + tailwind-scrollbar: "npm:^3.1.0" + tailwindcss: "npm:^3.4.6" + typescript: "npm:5.5.3" + zod: "npm:^3.23.8" + languageName: unknown + linkType: soft + +"@plunk/shared@npm:1.0.0, @plunk/shared@npm:^1.0.0, @plunk/shared@workspace:packages/shared": + version: 0.0.0-use.local + resolution: "@plunk/shared@workspace:packages/shared" + dependencies: + dayjs: "npm:^1.11.12" + typescript: "npm:^5.5.3" + zod: "npm:^3.23.8" + languageName: unknown + linkType: soft + +"@popperjs/core@npm:^2.9.0": + version: 2.11.8 + resolution: "@popperjs/core@npm:2.11.8" + checksum: 10c0/4681e682abc006d25eb380d0cf3efc7557043f53b6aea7a5057d0d1e7df849a00e281cd8ea79c902a35a414d7919621fc2ba293ecec05f413598e0b23d5a1e63 + languageName: node + linkType: hard + +"@prisma/client@npm:^5.17.0": + version: 5.17.0 + resolution: "@prisma/client@npm:5.17.0" + peerDependencies: + prisma: "*" + peerDependenciesMeta: + prisma: + optional: true + checksum: 10c0/cc6c5e9bfbc2f9a01fdf73e009c42298b8a9fea8c9b19db0089cad84a9ee94c3bb6f66f53f1e2f4b32b3506706bf16d23a8e3bcb4619a8bc76d0812a8382ae63 + languageName: node + linkType: hard + +"@prisma/debug@npm:5.17.0": + version: 5.17.0 + resolution: "@prisma/debug@npm:5.17.0" + checksum: 10c0/10aca89c8cd3a96c7f1153792110f33d96d1875e4af807002b9ca061eda255b1aa21e757b9e7a1690ac0676fb2312c441191cdb357acf45617dd658678984053 + languageName: node + linkType: hard + +"@prisma/engines-version@npm:5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053": + version: 5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053 + resolution: "@prisma/engines-version@npm:5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053" + checksum: 10c0/164b4cd6965da770bcd085fa0596466b092060d19eb8a4ba3402e66bd9b2e813cae417eeca99422b66a3a05a65cfe6d0e0339083b53644acf553ac138693232d + languageName: node + linkType: hard + +"@prisma/engines@npm:5.17.0": + version: 5.17.0 + resolution: "@prisma/engines@npm:5.17.0" + dependencies: + "@prisma/debug": "npm:5.17.0" + "@prisma/engines-version": "npm:5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053" + "@prisma/fetch-engine": "npm:5.17.0" + "@prisma/get-platform": "npm:5.17.0" + checksum: 10c0/b1d48c39fbe16680947685960be615894ccc1a2ca40263fc6d1ac4599e3100f2f31e71b02bd000c0f3269cd045f38817dfbddd37fefcb8a4dec6155a6df48e2f + languageName: node + linkType: hard + +"@prisma/fetch-engine@npm:5.17.0": + version: 5.17.0 + resolution: "@prisma/fetch-engine@npm:5.17.0" + dependencies: + "@prisma/debug": "npm:5.17.0" + "@prisma/engines-version": "npm:5.17.0-31.393aa359c9ad4a4bb28630fb5613f9c281cde053" + "@prisma/get-platform": "npm:5.17.0" + checksum: 10c0/b5c554e8a637871fd6497e656d67e649d9eea3a06be325b68a686b707c78d200ba9ba20bd76b0a3408e5cb78f6e34bab535ce161174273db377353a01368806e + languageName: node + linkType: hard + +"@prisma/get-platform@npm:5.17.0": + version: 5.17.0 + resolution: "@prisma/get-platform@npm:5.17.0" + dependencies: + "@prisma/debug": "npm:5.17.0" + checksum: 10c0/8687736c6e18737e29544bc1f98653b75b4dcb85c1ffe02686da100e843bb30041dd9d00146a2178517d34b783a650c8b76bdde5029d1675bd28c2be6ee6565a + languageName: node + linkType: hard + +"@remirror/core-constants@npm:^2.0.2": + version: 2.0.2 + resolution: "@remirror/core-constants@npm:2.0.2" + checksum: 10c0/928d12cc5df4fd5638980652a3aa398fa4a967fc4704f19eb717eb0fd60dd0e2ae5957a77ea0f6d42340b4c63839f311c54b49be56fba2bc24f102263498e4f8 + languageName: node + linkType: hard + +"@sigstore/bundle@npm:^2.3.2": + version: 2.3.2 + resolution: "@sigstore/bundle@npm:2.3.2" + dependencies: + "@sigstore/protobuf-specs": "npm:^0.3.2" + checksum: 10c0/872a95928236bd9950a2ecc66af1c60a82f6b482a62a20d0f817392d568a60739a2432cad70449ac01e44e9eaf85822d6d9ebc6ade6cb3e79a7d62226622eb5d + languageName: node + linkType: hard + +"@sigstore/core@npm:^1.0.0, @sigstore/core@npm:^1.1.0": + version: 1.1.0 + resolution: "@sigstore/core@npm:1.1.0" + checksum: 10c0/3b3420c1bd17de0371e1ac7c8f07a2cbcd24d6b49ace5bbf2b63f559ee08c4a80622a4d1c0ae42f2c9872166e9cb111f33f78bff763d47e5ef1efc62b8e457ea + languageName: node + linkType: hard + +"@sigstore/protobuf-specs@npm:^0.3.2": + version: 0.3.2 + resolution: "@sigstore/protobuf-specs@npm:0.3.2" + checksum: 10c0/108eed419181ff599763f2d28ff5087e7bce9d045919de548677520179fe77fb2e2b7290216c93c7a01bdb2972b604bf44599273c991bbdf628fbe1b9b70aacb + languageName: node + linkType: hard + +"@sigstore/sign@npm:^2.3.2": + version: 2.3.2 + resolution: "@sigstore/sign@npm:2.3.2" + dependencies: + "@sigstore/bundle": "npm:^2.3.2" + "@sigstore/core": "npm:^1.0.0" + "@sigstore/protobuf-specs": "npm:^0.3.2" + make-fetch-happen: "npm:^13.0.1" + proc-log: "npm:^4.2.0" + promise-retry: "npm:^2.0.1" + checksum: 10c0/a1e7908f3e4898f04db4d713fa10ddb3ae4f851592c9b554f1269073211e1417528b5088ecee60f27039fde5a5426ae573481d77cfd7e4395d2a0ddfcf5f365f + languageName: node + linkType: hard + +"@sigstore/tuf@npm:^2.3.4": + version: 2.3.4 + resolution: "@sigstore/tuf@npm:2.3.4" + dependencies: + "@sigstore/protobuf-specs": "npm:^0.3.2" + tuf-js: "npm:^2.2.1" + checksum: 10c0/97839882d787196517933df5505fae4634975807cc7adcd1783c7840c2a9729efb83ada47556ec326d544b9cb0d1851af990dc46eebb5fe7ea17bf7ce1fc0b8c + languageName: node + linkType: hard + +"@sigstore/verify@npm:^1.2.1": + version: 1.2.1 + resolution: "@sigstore/verify@npm:1.2.1" + dependencies: + "@sigstore/bundle": "npm:^2.3.2" + "@sigstore/core": "npm:^1.1.0" + "@sigstore/protobuf-specs": "npm:^0.3.2" + checksum: 10c0/af06580a8d5357c31259da1ac7323137054e0ac41e933278d95a4bc409a4463620125cb4c00b502f6bc32fdd68c2293019391b0d31ed921ee3852a9e84358628 + languageName: node + linkType: hard + +"@sinclair/typebox@npm:^0.27.8": + version: 0.27.8 + resolution: "@sinclair/typebox@npm:0.27.8" + checksum: 10c0/ef6351ae073c45c2ac89494dbb3e1f87cc60a93ce4cde797b782812b6f97da0d620ae81973f104b43c9b7eaa789ad20ba4f6a1359f1cc62f63729a55a7d22d4e + languageName: node + linkType: hard + +"@smithy/abort-controller@npm:^3.1.1": + version: 3.1.1 + resolution: "@smithy/abort-controller@npm:3.1.1" + dependencies: + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/914933d961b3b29db41a10b9040396968a738340d2bfd7f0b553521a91624ff86ee4ce7d97c15e3d94ca5e2b924da9dbefaf91e6cbd34db25d493690e4889f93 + languageName: node + linkType: hard + +"@smithy/chunked-blob-reader-native@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/chunked-blob-reader-native@npm:3.0.0" + dependencies: + "@smithy/util-base64": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 10c0/f3cbd03baaaf33a2c44a484851e3f2902f87cbb2168abff179276b19fd137be021393551b9270f9f3135408d816a06fe84ff826d9beb576dbe53fae9cf487362 + languageName: node + linkType: hard + +"@smithy/chunked-blob-reader@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/chunked-blob-reader@npm:3.0.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/cc551e4d6c711bec381d70c3074e3937ee78245bb15dd55c28c43c6c30808af1855c8df4a785a1033ded1483979ae115cf2c9decce73083346734db0d32b2fe5 + languageName: node + linkType: hard + +"@smithy/config-resolver@npm:^3.0.5": + version: 3.0.5 + resolution: "@smithy/config-resolver@npm:3.0.5" + dependencies: + "@smithy/node-config-provider": "npm:^3.1.4" + "@smithy/types": "npm:^3.3.0" + "@smithy/util-config-provider": "npm:^3.0.0" + "@smithy/util-middleware": "npm:^3.0.3" + tslib: "npm:^2.6.2" + checksum: 10c0/2346a0430a157660a759aee24fd20f18a9c4a3796938b1c792019a898afcdbb0af91af687b84f976a9f1e05eaba6946736e076f6b0ceb5f84b9063c67d2db8ae + languageName: node + linkType: hard + +"@smithy/core@npm:^2.2.7": + version: 2.2.8 + resolution: "@smithy/core@npm:2.2.8" + dependencies: + "@smithy/middleware-endpoint": "npm:^3.0.5" + "@smithy/middleware-retry": "npm:^3.0.11" + "@smithy/middleware-serde": "npm:^3.0.3" + "@smithy/protocol-http": "npm:^4.0.4" + "@smithy/smithy-client": "npm:^3.1.9" + "@smithy/types": "npm:^3.3.0" + "@smithy/util-middleware": "npm:^3.0.3" + tslib: "npm:^2.6.2" + checksum: 10c0/565978833c7f3698c00b1a330a2f4dc10f34abafb14a4ea42d7c78cf5f948c3f6cfec90be8fbe4f404774e220c02f432f4895f2a9a322722528642b2075638ac + languageName: node + linkType: hard + +"@smithy/credential-provider-imds@npm:^3.1.4": + version: 3.1.4 + resolution: "@smithy/credential-provider-imds@npm:3.1.4" + dependencies: + "@smithy/node-config-provider": "npm:^3.1.4" + "@smithy/property-provider": "npm:^3.1.3" + "@smithy/types": "npm:^3.3.0" + "@smithy/url-parser": "npm:^3.0.3" + tslib: "npm:^2.6.2" + checksum: 10c0/c05bb394ede243a165c51b717aaa050e7249a335bdccb3c413484eb2ce840f117eb74eb55a11ff6ecf81caf8b94b750b305afb2367c03a5c793d62da4124a7a0 + languageName: node + linkType: hard + +"@smithy/eventstream-codec@npm:^3.1.2": + version: 3.1.2 + resolution: "@smithy/eventstream-codec@npm:3.1.2" + dependencies: + "@aws-crypto/crc32": "npm:5.2.0" + "@smithy/types": "npm:^3.3.0" + "@smithy/util-hex-encoding": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 10c0/fc8db95d9625524b2832cf9cea203b4c1062197d04eef6f676b6eea06cc0007d45acb5270937c1b6b76f98638acaf0c2b822278226c25841ab45488df786e332 + languageName: node + linkType: hard + +"@smithy/eventstream-serde-browser@npm:^3.0.4": + version: 3.0.5 + resolution: "@smithy/eventstream-serde-browser@npm:3.0.5" + dependencies: + "@smithy/eventstream-serde-universal": "npm:^3.0.4" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/de7255f22fef702cb24d92be7eaea6f1c1faeeac21e1c6d6865df77c5018045033bb66e4658cf74856d1c7f7d4ad2fbb52d3e341705a5907eeee15c0449c0fa0 + languageName: node + linkType: hard + +"@smithy/eventstream-serde-config-resolver@npm:^3.0.3": + version: 3.0.3 + resolution: "@smithy/eventstream-serde-config-resolver@npm:3.0.3" + dependencies: + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/ef3360c0a0e4ad20f6e6da84b63e5071e3158af726bf291c610e2d42b5e042008cd9fe41ce2183f491422f23c36437987c0d1139e68b3c127d48c01b442dab82 + languageName: node + linkType: hard + +"@smithy/eventstream-serde-node@npm:^3.0.4": + version: 3.0.4 + resolution: "@smithy/eventstream-serde-node@npm:3.0.4" + dependencies: + "@smithy/eventstream-serde-universal": "npm:^3.0.4" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/11ff38048b1176625d4beb9ca245118aacaf867c90a94747e8cf0bb99e48c68aeedeab56c48a0238a27e35920c7074f3b6f71f8a8246a0d115962d728063a1f5 + languageName: node + linkType: hard + +"@smithy/eventstream-serde-universal@npm:^3.0.4": + version: 3.0.4 + resolution: "@smithy/eventstream-serde-universal@npm:3.0.4" + dependencies: + "@smithy/eventstream-codec": "npm:^3.1.2" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/99ab5f708fa4ebccea96b373395efc76b49c34ae8eb97aa33622ba82e93441a72010bb03693ec18d1517d9bb0a4a7e5c254179c22f38f411a6fecf8b3291c77f + languageName: node + linkType: hard + +"@smithy/fetch-http-handler@npm:^3.2.2": + version: 3.2.2 + resolution: "@smithy/fetch-http-handler@npm:3.2.2" + dependencies: + "@smithy/protocol-http": "npm:^4.0.4" + "@smithy/querystring-builder": "npm:^3.0.3" + "@smithy/types": "npm:^3.3.0" + "@smithy/util-base64": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 10c0/38155f095981537acc1e1a559b7d19cb584c388de7a82463d510553c40b9433168fcd39c946166440a2e8567f9e9a9490c8c3e85e5cc6cfd9486bfd19b466fba + languageName: node + linkType: hard + +"@smithy/hash-blob-browser@npm:^3.1.2": + version: 3.1.2 + resolution: "@smithy/hash-blob-browser@npm:3.1.2" + dependencies: + "@smithy/chunked-blob-reader": "npm:^3.0.0" + "@smithy/chunked-blob-reader-native": "npm:^3.0.0" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/71b017ae71839e058661e22589bacbc204d4980df66d67725aaa415493107e2f0898e41d0c6a4cd2c96333648d472c66ed35ec3c264156e6021bda5d590eb5ab + languageName: node + linkType: hard + +"@smithy/hash-node@npm:^3.0.3": + version: 3.0.3 + resolution: "@smithy/hash-node@npm:3.0.3" + dependencies: + "@smithy/types": "npm:^3.3.0" + "@smithy/util-buffer-from": "npm:^3.0.0" + "@smithy/util-utf8": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 10c0/d0ba0f069cb047a8a040733b9b119a194c130d287e8a68b8e79cf9cac5abe683df84ea28dd918e85a46031155e0d561f3c5854de3d280c3d501977a986550c8b + languageName: node + linkType: hard + +"@smithy/hash-stream-node@npm:^3.1.2": + version: 3.1.2 + resolution: "@smithy/hash-stream-node@npm:3.1.2" + dependencies: + "@smithy/types": "npm:^3.3.0" + "@smithy/util-utf8": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 10c0/2daadb5d6f08022ca1b1ecb4256d613613be86b7b768fb221ee3a2a7e584df0f4a546fba080e8366211c99f9ddb66d57e38525d10839405eab0b9d5be81d313b + languageName: node + linkType: hard + +"@smithy/invalid-dependency@npm:^3.0.3": + version: 3.0.3 + resolution: "@smithy/invalid-dependency@npm:3.0.3" + dependencies: + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/c52e909fa0cd8630e1e850da78af20abb11091b134ca107108e4f8336eee4b1b8cde60ba5946eff4bfe3d7bddc74e80a59fa0f448a7b45bf69df1e247aeee607 + languageName: node + linkType: hard + +"@smithy/is-array-buffer@npm:^2.2.0": + version: 2.2.0 + resolution: "@smithy/is-array-buffer@npm:2.2.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/2f2523cd8cc4538131e408eb31664983fecb0c8724956788b015aaf3ab85a0c976b50f4f09b176f1ed7bbe79f3edf80743be7a80a11f22cd9ce1285d77161aaf + languageName: node + linkType: hard + +"@smithy/is-array-buffer@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/is-array-buffer@npm:3.0.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/44710d94b9e6655ebc02169c149ea2bc5d5b9e509b6b39511cfe61bac571412290f4b9c743d61e395822f014021fcb709dbb533f2f717c1ac2d5a356696c22fd + languageName: node + linkType: hard + +"@smithy/md5-js@npm:^3.0.3": + version: 3.0.3 + resolution: "@smithy/md5-js@npm:3.0.3" + dependencies: + "@smithy/types": "npm:^3.3.0" + "@smithy/util-utf8": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 10c0/048b966676f5944da701120ca2e133de8a17fa403f2dc96dd88a82ea2248e2b439147b062ad8860486a9897899dd28de45cc0e2ae03c1221e2b987ad8e065464 + languageName: node + linkType: hard + +"@smithy/middleware-content-length@npm:^3.0.4": + version: 3.0.4 + resolution: "@smithy/middleware-content-length@npm:3.0.4" + dependencies: + "@smithy/protocol-http": "npm:^4.0.4" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/f6a847dc5ac09b91cdbcfeebf1fe67bbd6f79bc54a747b56be4912a0e600bae18e246dfe9529bd6d7812bb02391bfc01ba3cbafb28edf5d1419c61a3700ca885 + languageName: node + linkType: hard + +"@smithy/middleware-endpoint@npm:^3.0.5": + version: 3.0.5 + resolution: "@smithy/middleware-endpoint@npm:3.0.5" + dependencies: + "@smithy/middleware-serde": "npm:^3.0.3" + "@smithy/node-config-provider": "npm:^3.1.4" + "@smithy/shared-ini-file-loader": "npm:^3.1.4" + "@smithy/types": "npm:^3.3.0" + "@smithy/url-parser": "npm:^3.0.3" + "@smithy/util-middleware": "npm:^3.0.3" + tslib: "npm:^2.6.2" + checksum: 10c0/1820e52115a3312d4d9b915e7337c113590f12a41967d6b8f24bd5a033c1e16ca3b9419ff2ca9b8acfd106d210119b2ca5b8316b1150cbbf1827c4cb334d4551 + languageName: node + linkType: hard + +"@smithy/middleware-retry@npm:^3.0.10, @smithy/middleware-retry@npm:^3.0.11": + version: 3.0.11 + resolution: "@smithy/middleware-retry@npm:3.0.11" + dependencies: + "@smithy/node-config-provider": "npm:^3.1.4" + "@smithy/protocol-http": "npm:^4.0.4" + "@smithy/service-error-classification": "npm:^3.0.3" + "@smithy/smithy-client": "npm:^3.1.9" + "@smithy/types": "npm:^3.3.0" + "@smithy/util-middleware": "npm:^3.0.3" + "@smithy/util-retry": "npm:^3.0.3" + tslib: "npm:^2.6.2" + uuid: "npm:^9.0.1" + checksum: 10c0/fdc837748cbb454cc5993fd42bef1276a246efd410e42f2e28c5e6ebda2c23df202c8a33f233fea49d841d2bf83482c5a46401a73ec55058e59ed916a971c1d5 + languageName: node + linkType: hard + +"@smithy/middleware-serde@npm:^3.0.3": + version: 3.0.3 + resolution: "@smithy/middleware-serde@npm:3.0.3" + dependencies: + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/5b2ad50dea8af9a7a98816c0746c14af4267d053adcade9586a260cff968c41d768220b2987e5b751dbee7cd8c9538ff9839fbc7698dd09bf9b9ca4f5c8001ab + languageName: node + linkType: hard + +"@smithy/middleware-stack@npm:^3.0.3": + version: 3.0.3 + resolution: "@smithy/middleware-stack@npm:3.0.3" + dependencies: + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/c886d367ce02f6ae7bc70c4060e79ddfa46c3b35851921364836d64efb76f2fc71b0c1c09401c47d289dc93527a7699085a3feb0778e0337862aa8e6473cb54b + languageName: node + linkType: hard + +"@smithy/node-config-provider@npm:^3.1.4": + version: 3.1.4 + resolution: "@smithy/node-config-provider@npm:3.1.4" + dependencies: + "@smithy/property-provider": "npm:^3.1.3" + "@smithy/shared-ini-file-loader": "npm:^3.1.4" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/1d69cb8f83292df9e15523a727d55f6b812ff0ca30d615439cc6e7a5fe0d59c9524875745939bba611ca818757790f37509bb843b95f1e6d6b1ccd6d6c546077 + languageName: node + linkType: hard + +"@smithy/node-http-handler@npm:^3.1.3": + version: 3.1.3 + resolution: "@smithy/node-http-handler@npm:3.1.3" + dependencies: + "@smithy/abort-controller": "npm:^3.1.1" + "@smithy/protocol-http": "npm:^4.0.4" + "@smithy/querystring-builder": "npm:^3.0.3" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/02d90768a6b6b3351253b62bc00703c3c5980563408f77187165c79003dd53b791d1991bb9134c01ef3624358ab986c226d6f067d1415da6df8d6e8deaa39073 + languageName: node + linkType: hard + +"@smithy/property-provider@npm:^3.1.3": + version: 3.1.3 + resolution: "@smithy/property-provider@npm:3.1.3" + dependencies: + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/e1414e01f6efc298728ff79c1513f9606b44c00b98eb92d003e332ae7312ac9c0e1b7ef08ce426c99545100531fdc33efc0d769b6f75a953df015a8479e73f90 + languageName: node + linkType: hard + +"@smithy/protocol-http@npm:^4.0.4": + version: 4.0.4 + resolution: "@smithy/protocol-http@npm:4.0.4" + dependencies: + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/4859f9baab780204c18619ae61fc962949051380bbd70049c9affa3d248bd74ef1b7491351c7400d9bfe62e8c6e56bf07c373aec30efdca1443ac78f1630aa87 + languageName: node + linkType: hard + +"@smithy/querystring-builder@npm:^3.0.3": + version: 3.0.3 + resolution: "@smithy/querystring-builder@npm:3.0.3" + dependencies: + "@smithy/types": "npm:^3.3.0" + "@smithy/util-uri-escape": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 10c0/0fd88fb2f3b494981e286b840b7eeb90896d8cc2f47ce3964f65ae95eb74c82691af205bdc17abc39fd483e1952359459204686bb1741c9f425cd5a9a1503f65 + languageName: node + linkType: hard + +"@smithy/querystring-parser@npm:^3.0.3": + version: 3.0.3 + resolution: "@smithy/querystring-parser@npm:3.0.3" + dependencies: + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/a7bcbce8342ca520ca0dbbe420e93547c4eebf7193df4467bae5be6f0493492486a8dad6e20477c5f37f40b9903df91cb8bfb41ee1d21b63b5512f77291ffe6e + languageName: node + linkType: hard + +"@smithy/service-error-classification@npm:^3.0.3": + version: 3.0.3 + resolution: "@smithy/service-error-classification@npm:3.0.3" + dependencies: + "@smithy/types": "npm:^3.3.0" + checksum: 10c0/8ba7b655668fff01eb5de1d504711d6304d3e8a8dbbcb0620921bfdaafa5abca7621c0278d21367782d6c53277cddb8bbb6f9373013f64aac0c855520696bbd1 + languageName: node + linkType: hard + +"@smithy/shared-ini-file-loader@npm:^3.1.4": + version: 3.1.4 + resolution: "@smithy/shared-ini-file-loader@npm:3.1.4" + dependencies: + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/e90e5e375fc5afb4dda335e1d0a9d3496cec731511c35351330a210dc22d22b398c45e49d3a4142e55ce7d0e1b280d1b3d46cecdd97b9527f2d9e89ced74f63b + languageName: node + linkType: hard + +"@smithy/signature-v4@npm:^4.0.0": + version: 4.0.0 + resolution: "@smithy/signature-v4@npm:4.0.0" + dependencies: + "@smithy/is-array-buffer": "npm:^3.0.0" + "@smithy/types": "npm:^3.3.0" + "@smithy/util-hex-encoding": "npm:^3.0.0" + "@smithy/util-middleware": "npm:^3.0.3" + "@smithy/util-uri-escape": "npm:^3.0.0" + "@smithy/util-utf8": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 10c0/62a6ce9a3e85df4c91c1d4086dbb1d6d229405927bc0f8dce1b179b105841991992bd0fb1b524c7e3ee8f8f248381b7f812f994c72cecb22ddaf52a2e998a6e8 + languageName: node + linkType: hard + +"@smithy/smithy-client@npm:^3.1.8, @smithy/smithy-client@npm:^3.1.9": + version: 3.1.9 + resolution: "@smithy/smithy-client@npm:3.1.9" + dependencies: + "@smithy/middleware-endpoint": "npm:^3.0.5" + "@smithy/middleware-stack": "npm:^3.0.3" + "@smithy/protocol-http": "npm:^4.0.4" + "@smithy/types": "npm:^3.3.0" + "@smithy/util-stream": "npm:^3.1.1" + tslib: "npm:^2.6.2" + checksum: 10c0/1986cf1054c36c4da94d87680ed4bbe9cf55bcf6a65b0ec6398076128d9fc546b7528528a25d150bdaebc15f94e93e13dd313daf82d6d8ecbcae10db0cf9e3e9 + languageName: node + linkType: hard + +"@smithy/types@npm:^3.3.0": + version: 3.3.0 + resolution: "@smithy/types@npm:3.3.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/ab2c2d621384a2bbdd31d5c90809395cb5c2a726afd69758895d5a630f932f6ae9a53ca7a9cd5d8c195df9278869b2420a2fb4fada47dee9e8c9d4e3c80a349e + languageName: node + linkType: hard + +"@smithy/url-parser@npm:^3.0.3": + version: 3.0.3 + resolution: "@smithy/url-parser@npm:3.0.3" + dependencies: + "@smithy/querystring-parser": "npm:^3.0.3" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/9ed0ab14034369fd823587c22d22e257203638a327954853c9bb92c3571a94fa7dc56211f9340b0ac3af5c37dfa206fd99dcde4ee9164a300994314a83e0b042 + languageName: node + linkType: hard + +"@smithy/util-base64@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/util-base64@npm:3.0.0" + dependencies: + "@smithy/util-buffer-from": "npm:^3.0.0" + "@smithy/util-utf8": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 10c0/5c05c3505bd1ac4c1e04ec0e22ad1c9e0c61756945735861614f9e46146369a1a112dd0895602475822c18b8f1fe0cc3fb9e45c99a4e7fb03308969c673cf043 + languageName: node + linkType: hard + +"@smithy/util-body-length-browser@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/util-body-length-browser@npm:3.0.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/cfb595e814334fe7bb78e8381141cc7364f66bff0c1d672680f4abb99361ef66fbdb9468fa1dbabcd5753254b2b05c59c907fa9d600b36e6e4b8423eccf412f7 + languageName: node + linkType: hard + +"@smithy/util-body-length-node@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/util-body-length-node@npm:3.0.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/6f779848e7c81051364cf6e40ed61034a06fa8df3480398528baae54d9b69622abc7d068869e33dbe51fef2bbc6fda3f548ac59644a0f10545a54c87bc3a4391 + languageName: node + linkType: hard + +"@smithy/util-buffer-from@npm:^2.2.0": + version: 2.2.0 + resolution: "@smithy/util-buffer-from@npm:2.2.0" + dependencies: + "@smithy/is-array-buffer": "npm:^2.2.0" + tslib: "npm:^2.6.2" + checksum: 10c0/223d6a508b52ff236eea01cddc062b7652d859dd01d457a4e50365af3de1e24a05f756e19433f6ccf1538544076b4215469e21a4ea83dc1d58d829725b0dbc5a + languageName: node + linkType: hard + +"@smithy/util-buffer-from@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/util-buffer-from@npm:3.0.0" + dependencies: + "@smithy/is-array-buffer": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 10c0/b10fb81ef34f95418f27c9123c2c1774e690dd447e8064184688c553156bdec46d2ba1b1ae3bad7edd2b58a5ef32ac569e1ad814b36e7ee05eba10526d329983 + languageName: node + linkType: hard + +"@smithy/util-config-provider@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/util-config-provider@npm:3.0.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/a2c25eac31223eddea306beff2bb3c32e8761f8cb50e8cb2a9d61417a5040e9565dc715a655787e99a37465fdd35bbd0668ff36e06043a5f6b7be48a76974792 + languageName: node + linkType: hard + +"@smithy/util-defaults-mode-browser@npm:^3.0.10": + version: 3.0.11 + resolution: "@smithy/util-defaults-mode-browser@npm:3.0.11" + dependencies: + "@smithy/property-provider": "npm:^3.1.3" + "@smithy/smithy-client": "npm:^3.1.9" + "@smithy/types": "npm:^3.3.0" + bowser: "npm:^2.11.0" + tslib: "npm:^2.6.2" + checksum: 10c0/61da2e26894c990a25511eb2a58f13a7295dacd54f6848b8fad572d069d5ecdc0d4d955a1167fb7fbfe28ed478c748d2f31fda9b7990bf96aa44c9aba45b1576 + languageName: node + linkType: hard + +"@smithy/util-defaults-mode-node@npm:^3.0.10": + version: 3.0.11 + resolution: "@smithy/util-defaults-mode-node@npm:3.0.11" + dependencies: + "@smithy/config-resolver": "npm:^3.0.5" + "@smithy/credential-provider-imds": "npm:^3.1.4" + "@smithy/node-config-provider": "npm:^3.1.4" + "@smithy/property-provider": "npm:^3.1.3" + "@smithy/smithy-client": "npm:^3.1.9" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/ca792300507d141ebaf406d100efa116e923009afffe582b4d3ad6327084e47b3d67c2c1a8f6ec5d8586b300bab4432a9426c663b04a1e04180a5214b57e5c8f + languageName: node + linkType: hard + +"@smithy/util-endpoints@npm:^2.0.5": + version: 2.0.5 + resolution: "@smithy/util-endpoints@npm:2.0.5" + dependencies: + "@smithy/node-config-provider": "npm:^3.1.4" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/4dd0740eaca169dc1078ef7e10dd0b0cc186e8c2bb1bf26c7ab8dff557c59f146bf6496a3e44a7bbb9ac6bfbcb587f1a100d81466f29b20dbb58e3e5cf5bceeb + languageName: node + linkType: hard + +"@smithy/util-hex-encoding@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/util-hex-encoding@npm:3.0.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/d2fa7270853cc8f22c4f4635c72bf52e303731a68a3999e3ea9da1d38b6bf08c0f884e7d20b65741e3bc68bb3821e1abd1c3406d7a3dce8fc02df019aea59162 + languageName: node + linkType: hard + +"@smithy/util-middleware@npm:^3.0.3": + version: 3.0.3 + resolution: "@smithy/util-middleware@npm:3.0.3" + dependencies: + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/1d7d01f75ab6d116e6d539bbcfc6f5d7f2b6e3a25f970758872a2e45c4a6b5795326d2f51b2566ca9fe5ba260d9176b33260bde15759c5296ab9f8557835364e + languageName: node + linkType: hard + +"@smithy/util-retry@npm:^3.0.3": + version: 3.0.3 + resolution: "@smithy/util-retry@npm:3.0.3" + dependencies: + "@smithy/service-error-classification": "npm:^3.0.3" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/bea28dff13ae32222dda579eb9bccfaf34b427ab46165509cd524a7080463361a39acc5d1aa7452714c38193a5523f3ab810cd2e60eef9bc768fd1ab23b5bde6 + languageName: node + linkType: hard + +"@smithy/util-stream@npm:^3.1.0, @smithy/util-stream@npm:^3.1.1": + version: 3.1.1 + resolution: "@smithy/util-stream@npm:3.1.1" + dependencies: + "@smithy/fetch-http-handler": "npm:^3.2.2" + "@smithy/node-http-handler": "npm:^3.1.3" + "@smithy/types": "npm:^3.3.0" + "@smithy/util-base64": "npm:^3.0.0" + "@smithy/util-buffer-from": "npm:^3.0.0" + "@smithy/util-hex-encoding": "npm:^3.0.0" + "@smithy/util-utf8": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 10c0/3b338fe65b9af404b4428e567cf136f8ba2b5d24dfaab1dddfa92f2688247af00c0d47d2a71bf9e9cdaab0d4ebae7514c928e497927f29bc23d2c873d6dce176 + languageName: node + linkType: hard + +"@smithy/util-uri-escape@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/util-uri-escape@npm:3.0.0" + dependencies: + tslib: "npm:^2.6.2" + checksum: 10c0/b8d831348412cfafd9300069e74a12e0075b5e786d7ef6a210ba4ab576001c2525653eec68b71dfe6d7aef71c52f547404c4f0345c0fb476a67277f9d44b1156 + languageName: node + linkType: hard + +"@smithy/util-utf8@npm:^2.0.0": + version: 2.3.0 + resolution: "@smithy/util-utf8@npm:2.3.0" + dependencies: + "@smithy/util-buffer-from": "npm:^2.2.0" + tslib: "npm:^2.6.2" + checksum: 10c0/e18840c58cc507ca57fdd624302aefd13337ee982754c9aa688463ffcae598c08461e8620e9852a424d662ffa948fc64919e852508028d09e89ced459bd506ab + languageName: node + linkType: hard + +"@smithy/util-utf8@npm:^3.0.0": + version: 3.0.0 + resolution: "@smithy/util-utf8@npm:3.0.0" + dependencies: + "@smithy/util-buffer-from": "npm:^3.0.0" + tslib: "npm:^2.6.2" + checksum: 10c0/b568ed84b4770d2ae9b632eb85603765195a791f045af7f47df1369dc26b001056f4edf488b42ca1cd6d852d0155ad306a0d6531e912cb4e633c0d87abaa8899 + languageName: node + linkType: hard + +"@smithy/util-waiter@npm:^3.1.2": + version: 3.1.2 + resolution: "@smithy/util-waiter@npm:3.1.2" + dependencies: + "@smithy/abort-controller": "npm:^3.1.1" + "@smithy/types": "npm:^3.3.0" + tslib: "npm:^2.6.2" + checksum: 10c0/50e7ef8de9779650aec125b81b28e01e9b696f121841d6b1037fd7a2e1296db21c2399b3cf87381a256b3db04a63013c65dba187d22d2a38d31e389ef356c066 + languageName: node + linkType: hard + +"@svgr/babel-plugin-add-jsx-attribute@npm:8.0.0": + version: 8.0.0 + resolution: "@svgr/babel-plugin-add-jsx-attribute@npm:8.0.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/a50bd0baa34faf16bcba712091f94c7f0e230431fe99a9dfc3401fa92823ad3f68495b86ab9bf9044b53839e8c416cfbb37eb3f246ff33f261e0fa9ee1779c5b + languageName: node + linkType: hard + +"@svgr/babel-plugin-remove-jsx-attribute@npm:8.0.0": + version: 8.0.0 + resolution: "@svgr/babel-plugin-remove-jsx-attribute@npm:8.0.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/8a98e59bd9971e066815b4129409932f7a4db4866834fe75677ea6d517972fb40b380a69a4413189f20e7947411f9ab1b0f029dd5e8068686a5a0188d3ccd4c7 + languageName: node + linkType: hard + +"@svgr/babel-plugin-remove-jsx-empty-expression@npm:8.0.0": + version: 8.0.0 + resolution: "@svgr/babel-plugin-remove-jsx-empty-expression@npm:8.0.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/517dcca75223bd05d3f056a8514dbba3031278bea4eadf0842c576d84f4651e7a4e0e7082d3ee4ef42456de0f9c4531d8a1917c04876ca64b014b859ca8f1bde + languageName: node + linkType: hard + +"@svgr/babel-plugin-replace-jsx-attribute-value@npm:8.0.0": + version: 8.0.0 + resolution: "@svgr/babel-plugin-replace-jsx-attribute-value@npm:8.0.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/004bd1892053b7e9c1b0bb14acc44e77634ec393722b87b1e4fae53e2c35122a2dd0d5c15e9070dbeec274e22e7693a2b8b48506733a8009ee92b12946fcb10a + languageName: node + linkType: hard + +"@svgr/babel-plugin-svg-dynamic-title@npm:8.0.0": + version: 8.0.0 + resolution: "@svgr/babel-plugin-svg-dynamic-title@npm:8.0.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/80e0a7fcf902f984c705051ca5c82ea6050ccbb70b651a8fea6d0eb5809e4dac274b49ea6be2d87f1eb9dfc0e2d6cdfffe1669ec2117f44b67a60a07d4c0b8b8 + languageName: node + linkType: hard + +"@svgr/babel-plugin-svg-em-dimensions@npm:8.0.0": + version: 8.0.0 + resolution: "@svgr/babel-plugin-svg-em-dimensions@npm:8.0.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/73e92c8277a89279745c0c500f59f083279a8dc30cd552b22981fade2a77628fb2bd2819ee505725fcd2e93f923e3790b52efcff409a159e657b46604a0b9a21 + languageName: node + linkType: hard + +"@svgr/babel-plugin-transform-react-native-svg@npm:8.1.0": + version: 8.1.0 + resolution: "@svgr/babel-plugin-transform-react-native-svg@npm:8.1.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/655ed6bc7a208ceaa4ecff0a54ccc36008c3cb31efa90d11e171cab325ebbb21aa78f09c7b65f9b3ddeda3a85f348c0c862902c48be13c14b4de165c847974e3 + languageName: node + linkType: hard + +"@svgr/babel-plugin-transform-svg-component@npm:8.0.0": + version: 8.0.0 + resolution: "@svgr/babel-plugin-transform-svg-component@npm:8.0.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/4ac00bb99a3db4ef05e4362f116a3c608ee365a2d26cf7318d8d41a4a5b30a02c80455cce0e62c65b60ed815b5d632bedabac2ccd4b56f998fadef5286e3ded4 + languageName: node + linkType: hard + +"@svgr/babel-preset@npm:8.1.0": + version: 8.1.0 + resolution: "@svgr/babel-preset@npm:8.1.0" + dependencies: + "@svgr/babel-plugin-add-jsx-attribute": "npm:8.0.0" + "@svgr/babel-plugin-remove-jsx-attribute": "npm:8.0.0" + "@svgr/babel-plugin-remove-jsx-empty-expression": "npm:8.0.0" + "@svgr/babel-plugin-replace-jsx-attribute-value": "npm:8.0.0" + "@svgr/babel-plugin-svg-dynamic-title": "npm:8.0.0" + "@svgr/babel-plugin-svg-em-dimensions": "npm:8.0.0" + "@svgr/babel-plugin-transform-react-native-svg": "npm:8.1.0" + "@svgr/babel-plugin-transform-svg-component": "npm:8.0.0" + peerDependencies: + "@babel/core": ^7.0.0-0 + checksum: 10c0/49367d3ad0831f79b1056871b91766246f449d4d1168623af5e283fbaefce4a01d77ab00de6b045b55e956f9aae27895823198493cd232d88d3435ea4517ffc5 + languageName: node + linkType: hard + +"@svgr/core@npm:8.1.0": + version: 8.1.0 + resolution: "@svgr/core@npm:8.1.0" + dependencies: + "@babel/core": "npm:^7.21.3" + "@svgr/babel-preset": "npm:8.1.0" + camelcase: "npm:^6.2.0" + cosmiconfig: "npm:^8.1.3" + snake-case: "npm:^3.0.4" + checksum: 10c0/6a2f6b1bc79bce39f66f088d468985d518005fc5147ebf4f108570a933818b5951c2cb7da230ddff4b7c8028b5a672b2d33aa2acce012b8b9770073aa5a2d041 + languageName: node + linkType: hard + +"@svgr/hast-util-to-babel-ast@npm:8.0.0": + version: 8.0.0 + resolution: "@svgr/hast-util-to-babel-ast@npm:8.0.0" + dependencies: + "@babel/types": "npm:^7.21.3" + entities: "npm:^4.4.0" + checksum: 10c0/f4165b583ba9eaf6719e598977a7b3ed182f177983e55f9eb55a6a73982d81277510e9eb7ab41f255151fb9ed4edd11ac4bef95dd872f04ed64966d8c85e0f79 + languageName: node + linkType: hard + +"@svgr/plugin-jsx@npm:8.1.0": + version: 8.1.0 + resolution: "@svgr/plugin-jsx@npm:8.1.0" + dependencies: + "@babel/core": "npm:^7.21.3" + "@svgr/babel-preset": "npm:8.1.0" + "@svgr/hast-util-to-babel-ast": "npm:8.0.0" + svg-parser: "npm:^2.0.4" + peerDependencies: + "@svgr/core": "*" + checksum: 10c0/07b4d9e00de795540bf70556fa2cc258774d01e97a12a26234c6fdf42b309beb7c10f31ee24d1a71137239347b1547b8bb5587d3a6de10669f95dcfe99cddc56 + languageName: node + linkType: hard + +"@svgr/plugin-svgo@npm:8.1.0": + version: 8.1.0 + resolution: "@svgr/plugin-svgo@npm:8.1.0" + dependencies: + cosmiconfig: "npm:^8.1.3" + deepmerge: "npm:^4.3.1" + svgo: "npm:^3.0.2" + peerDependencies: + "@svgr/core": "*" + checksum: 10c0/bfd25460f23f1548bfb8f6f3bedd6d6972c1a4f8881bd35a4f8c115218da6e999e8f9ac0ef0ed88c4e0b93fcec37f382b94c0322f4ec2b26752a89e5cc8b9d7a + languageName: node + linkType: hard + +"@svgr/webpack@npm:^8.1.0": + version: 8.1.0 + resolution: "@svgr/webpack@npm:8.1.0" + dependencies: + "@babel/core": "npm:^7.21.3" + "@babel/plugin-transform-react-constant-elements": "npm:^7.21.3" + "@babel/preset-env": "npm:^7.20.2" + "@babel/preset-react": "npm:^7.18.6" + "@babel/preset-typescript": "npm:^7.21.0" + "@svgr/core": "npm:8.1.0" + "@svgr/plugin-jsx": "npm:8.1.0" + "@svgr/plugin-svgo": "npm:8.1.0" + checksum: 10c0/4c1cac45bd5890de8643e5a7bfb71f3bcd8b85ae5bbacf10b8ad9f939b7a98e8d601c3ada204ffb95223abf4a24beeac5a2a0d6928a52a1ab72a29da3c015c22 + languageName: node + linkType: hard + +"@swc/counter@npm:^0.1.3": + version: 0.1.3 + resolution: "@swc/counter@npm:0.1.3" + checksum: 10c0/8424f60f6bf8694cfd2a9bca45845bce29f26105cda8cf19cdb9fd3e78dc6338699e4db77a89ae449260bafa1cc6bec307e81e7fb96dbf7dcfce0eea55151356 + languageName: node + linkType: hard + +"@swc/helpers@npm:0.5.5": + version: 0.5.5 + resolution: "@swc/helpers@npm:0.5.5" + dependencies: + "@swc/counter": "npm:^0.1.3" + tslib: "npm:^2.4.0" + checksum: 10c0/21a9b9cfe7e00865f9c9f3eb4c1cc5b397143464f7abee76a2c5366e591e06b0155b5aac93fe8269ef8d548df253f6fd931e9ddfc0fd12efd405f90f45506e7d + languageName: node + linkType: hard + +"@tailwindcss/aspect-ratio@npm:^0.4.2": + version: 0.4.2 + resolution: "@tailwindcss/aspect-ratio@npm:0.4.2" + peerDependencies: + tailwindcss: ">=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1" + checksum: 10c0/c821a8061f80200b20db26f9cf2a711ffb17814adf847a72d4d4632804d1cabf6671df8e99e06cedfb2ba97b968d7843cd435d1201519c86acdde702efca418b + languageName: node + linkType: hard + +"@tailwindcss/forms@npm:^0.5.7": + version: 0.5.7 + resolution: "@tailwindcss/forms@npm:0.5.7" + dependencies: + mini-svg-data-uri: "npm:^1.2.3" + peerDependencies: + tailwindcss: ">=3.0.0 || >= 3.0.0-alpha.1" + checksum: 10c0/cd29e0c978402ae87a923ae802dcff43f7b050595666cb067321cac2e37a52f61b9d73385cb0a10455548581ddd0d3886815bd6c64a1da06247c0057fa9f4601 + languageName: node + linkType: hard + +"@tailwindcss/typography@npm:^0.5.13": + version: 0.5.13 + resolution: "@tailwindcss/typography@npm:0.5.13" + dependencies: + lodash.castarray: "npm:^4.4.0" + lodash.isplainobject: "npm:^4.0.6" + lodash.merge: "npm:^4.6.2" + postcss-selector-parser: "npm:6.0.10" + peerDependencies: + tailwindcss: "*" + checksum: 10c0/6c01287e7492c001595cd5a39765f313e48e1d2997ea78823919edabd692300d144c42b6e16dee6e077a683e635b9164ff985d5a0f8eeff7824b2d119151899e + languageName: node + linkType: hard + +"@tippyjs/react@npm:^4.2.6": + version: 4.2.6 + resolution: "@tippyjs/react@npm:4.2.6" + dependencies: + tippy.js: "npm:^6.3.1" + peerDependencies: + react: ">=16.8" + react-dom: ">=16.8" + checksum: 10c0/b174f2fbd27c16c5a8554ee8b26f3cc61bc37507669a1cef3e3333bfb3db85c84a57a93003c972ede8007786cf0e813d489781aa9caf46fb3bf1b851e3f4daba + languageName: node + linkType: hard + +"@tiptap/core@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/core@npm:2.5.4" + peerDependencies: + "@tiptap/pm": ^2.5.4 + checksum: 10c0/45d6be669c40f1e0a030af9e6de5a7f9ec74ca254a2497d231c5bfb993307138a1e238b0a92150f65862c76b488606e87cf13264710efa69959998a3f94267cc + languageName: node + linkType: hard + +"@tiptap/extension-blockquote@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-blockquote@npm:2.5.4" + peerDependencies: + "@tiptap/core": ^2.5.4 + checksum: 10c0/16bd0ef1ff9d8da7b2ad01e43351845080bfa35b803987b16c6a446508b4c643602551cc1572682a00f2a23de2301cef7ba60b50e1667d2b7da953fdd4593c75 + languageName: node + linkType: hard + +"@tiptap/extension-bold@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-bold@npm:2.5.4" + peerDependencies: + "@tiptap/core": ^2.5.4 + checksum: 10c0/15cce4db9cf4a16430ab29d40e2921395b4c549247cf4051ad19bac52873e0ccd1b7dbb760ad73d51cbeacecc82568c05ae4e2e12048ae22f077c9502e8d134d + languageName: node + linkType: hard + +"@tiptap/extension-bubble-menu@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-bubble-menu@npm:2.5.4" + dependencies: + tippy.js: "npm:^6.3.7" + peerDependencies: + "@tiptap/core": ^2.5.4 + "@tiptap/pm": ^2.5.4 + checksum: 10c0/5d9b072e220c4487ad66334d57d43e92d9bb1739aded3a63327d6742fc5d3fccce4ffdac875f04db6674d9719f7819ef323c6f1d56b0a0d6964957c78e5f2704 + languageName: node + linkType: hard + +"@tiptap/extension-bullet-list@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-bullet-list@npm:2.5.4" + peerDependencies: + "@tiptap/core": ^2.5.4 + checksum: 10c0/b913b8fc4708a9413f74e460ece64b5fcc5214d88712899045e7fa929ce04c4bcc057cee18b5d6ff80ace35897e3a391cd34cb91d6cacdf9b0b35eb312966cbf + languageName: node + linkType: hard + +"@tiptap/extension-code-block@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-code-block@npm:2.5.4" + peerDependencies: + "@tiptap/core": ^2.5.4 + "@tiptap/pm": ^2.5.4 + checksum: 10c0/4929797c3cdb67ed560d47eb5b43f1e05a666dd6607972608c08ad2326f48d79b9676177759beb2b5198ef3b0718d338638c326bbe5abe5a09c88b2bc886a7d7 + languageName: node + linkType: hard + +"@tiptap/extension-code@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-code@npm:2.5.4" + peerDependencies: + "@tiptap/core": ^2.5.4 + checksum: 10c0/06c6103ba943668ac62e831c24e320647e3f2cb20b53584306113b089efbf3bb3a09d47bb507fa503d344a6b216167dc50f2f5cb70470150ab6f92de4058ec78 + languageName: node + linkType: hard + +"@tiptap/extension-color@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-color@npm:2.5.4" + peerDependencies: + "@tiptap/core": ^2.5.4 + "@tiptap/extension-text-style": ^2.5.4 + checksum: 10c0/e907010c4ec7b14fc946da85919f805c5d9976e6f2f8a7e1a6d87d689a6fcef1838fe3056ff6cd8a4ed2f085b8797cfabbbbe94c2d7ef6a38489fb83f071fd36 + languageName: node + linkType: hard + +"@tiptap/extension-document@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-document@npm:2.5.4" + peerDependencies: + "@tiptap/core": ^2.5.4 + checksum: 10c0/d4d873530df79e5525c3a12f67258fda706a2a467b952f33a5ec2d6046b8a419bcff45ee41890fd5a07f17051cf13b25709ca9fafb99880add84e1f9dbd5a357 + languageName: node + linkType: hard + +"@tiptap/extension-dropcursor@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-dropcursor@npm:2.5.4" + peerDependencies: + "@tiptap/core": ^2.5.4 + "@tiptap/pm": ^2.5.4 + checksum: 10c0/ca8ba1ca5f3897814176b4174bde575ba82a709a80936a0f8c37bb2eeddd1a3c3ee8858d8541df6ed29cf5ae22e0a9265974a2065bfd29f63e501a42f098ae97 + languageName: node + linkType: hard + +"@tiptap/extension-floating-menu@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-floating-menu@npm:2.5.4" + dependencies: + tippy.js: "npm:^6.3.7" + peerDependencies: + "@tiptap/core": ^2.5.4 + "@tiptap/pm": ^2.5.4 + checksum: 10c0/c5531ff5c7623d1e98c136baa45863cc7bd5e948a972b53c5fa633c0346ce948ecb41a04cc0554e5429a387e313415de340a79fed76daa68503e86c5f65a89a1 + languageName: node + linkType: hard + +"@tiptap/extension-font-family@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-font-family@npm:2.5.4" + peerDependencies: + "@tiptap/core": ^2.5.4 + "@tiptap/extension-text-style": ^2.5.4 + checksum: 10c0/edfaba169877df9b562faef267cb6263ac82c7dd7709ccf82abb3af2499d7ab443fec89ef54554bdbd65337d05c7b8c608e0f48660d800556d1a42ccd5104150 + languageName: node + linkType: hard + +"@tiptap/extension-gapcursor@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-gapcursor@npm:2.5.4" + peerDependencies: + "@tiptap/core": ^2.5.4 + "@tiptap/pm": ^2.5.4 + checksum: 10c0/cb0e2212c964dacc9a88396b7fa0f5203a6e26bf1e8030bd3a7e5681a2db73586c73a5b9f97258420dd62d628265fcdf8f79e82ed5ab693390738b8907c27608 + languageName: node + linkType: hard + +"@tiptap/extension-hard-break@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-hard-break@npm:2.5.4" + peerDependencies: + "@tiptap/core": ^2.5.4 + checksum: 10c0/0cf7026a774e722e9bffd5cb0a2805cb2f1a0e4bcad173107c7731ac41bfe7119c15a487c89c5b28c8dcca9e50ace6c1ee9c1c36e35a075d69e814f3f1d84b6b + languageName: node + linkType: hard + +"@tiptap/extension-heading@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-heading@npm:2.5.4" + peerDependencies: + "@tiptap/core": ^2.5.4 + checksum: 10c0/524390922011df169b4b0fc782e6082a8e6951b3a01d651fd70d6c6592c1affb37ded2c46ef9b444e07a3aeea69191096c318ed5b8485f8f98cec97b324541e3 + languageName: node + linkType: hard + +"@tiptap/extension-history@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-history@npm:2.5.4" + peerDependencies: + "@tiptap/core": ^2.5.4 + "@tiptap/pm": ^2.5.4 + checksum: 10c0/6fbd8b37fa9e1365006bc453296af56c006dba8e280555e071ecb6c0ed1b1dc36d6a4288991ee3d0dfd46a4ae0531d2477113c1e703205f945a37fc2e2305ce2 + languageName: node + linkType: hard + +"@tiptap/extension-horizontal-rule@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-horizontal-rule@npm:2.5.4" + peerDependencies: + "@tiptap/core": ^2.5.4 + "@tiptap/pm": ^2.5.4 + checksum: 10c0/f17ca3aaace5fb669c65a698337cc7a98c7f80ae88cef653b31d5b021897a59492507474a674c75739cace43092ab86967d3674ddbae3972109e382c417cdcd9 + languageName: node + linkType: hard + +"@tiptap/extension-image@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-image@npm:2.5.4" + peerDependencies: + "@tiptap/core": ^2.5.4 + checksum: 10c0/5719dbf30ac957016a588a3176aac6bbf466ff192297e163b9b9aebb2dc5872da4f1cb854be599d94d5a2c461006e33de112f3c35a942536c8d7d46929110ba6 + languageName: node + linkType: hard + +"@tiptap/extension-italic@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-italic@npm:2.5.4" + peerDependencies: + "@tiptap/core": ^2.5.4 + checksum: 10c0/324fac3e6d351746bff2b2ffe83c4bc8a4c08ba1c29e6e1804688d153c3347c90ac3d323d10ae726fc12923f38e54e6bccc7d18a625a6a8c5aa7ae95f407e7b6 + languageName: node + linkType: hard + +"@tiptap/extension-link@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-link@npm:2.5.4" + dependencies: + linkifyjs: "npm:^4.1.0" + peerDependencies: + "@tiptap/core": ^2.5.4 + "@tiptap/pm": ^2.5.4 + checksum: 10c0/90e2b18dd297151382339607e41cc55d4bf2af0146307d5236717d2aa9fe5f25c2d9ae5e362989475a947f91865d2f7c94202646ba5c893c0e3b9a55905fb834 + languageName: node + linkType: hard + +"@tiptap/extension-list-item@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-list-item@npm:2.5.4" + peerDependencies: + "@tiptap/core": ^2.5.4 + checksum: 10c0/36f220ff5c750ca45480ef6f3a652b0fe59db83a0a42e4735f653a079a57bee17b6770c403f1c76818a3db82177f7acd95afe1f3198099929609baed60957364 + languageName: node + linkType: hard + +"@tiptap/extension-ordered-list@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-ordered-list@npm:2.5.4" + peerDependencies: + "@tiptap/core": ^2.5.4 + checksum: 10c0/571a6147eeb9cefa95087a73fefe47a75b226b111ebad63457243e98d73dc88c88c6a73b2167eda6bf21d050b338aa6fb54b123742c8a7bddda02c2dea33771a + languageName: node + linkType: hard + +"@tiptap/extension-paragraph@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-paragraph@npm:2.5.4" + peerDependencies: + "@tiptap/core": ^2.5.4 + checksum: 10c0/0342afd89447b51d9e3c70ae221d684ef9e44e7ebfc0c8c12119c006b44da499e1827c7b84de4faf97c19cd9a13ce17e8b4236d5cb649fcc7777f9f6efa85c61 + languageName: node + linkType: hard + +"@tiptap/extension-placeholder@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-placeholder@npm:2.5.4" + peerDependencies: + "@tiptap/core": ^2.5.4 + "@tiptap/pm": ^2.5.4 + checksum: 10c0/0c49f401a24f1f0ffeb4c08fe9807e2029ece6505a175cdeea318977263e4adf119187852b80eff6922b27cc38adfa92849da7f8c0cfb4c1a1a8b3fc2bbe9647 + languageName: node + linkType: hard + +"@tiptap/extension-strike@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-strike@npm:2.5.4" + peerDependencies: + "@tiptap/core": ^2.5.4 + checksum: 10c0/c7ed02dc0ed52d783ad257005d1a963d3399df84a37076603825dd0802d96597814e369cbf9726d45c4d9e1eb4e9001b83aa24bbf5e2f33f6b28fd12e86f1388 + languageName: node + linkType: hard + +"@tiptap/extension-text-align@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-text-align@npm:2.5.4" + peerDependencies: + "@tiptap/core": ^2.5.4 + checksum: 10c0/44b7c0cc217a83127ab5c3496dce672c8b2018469c24a70bfbc90a770ac9924f2cc393a8dc3ff6b1e9d6847bff104a20b3b71d83c0acdfc32469e410a40766f9 + languageName: node + linkType: hard + +"@tiptap/extension-text-style@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-text-style@npm:2.5.4" + peerDependencies: + "@tiptap/core": ^2.5.4 + checksum: 10c0/a775c736774d267c352d86c90ac30bba49a94ad334cf9e36493975f4a427bc91962a5b721ca269ecd92b7578b43189676ec1dbc94a55fd68513b599786ab264f + languageName: node + linkType: hard + +"@tiptap/extension-text@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-text@npm:2.5.4" + peerDependencies: + "@tiptap/core": ^2.5.4 + checksum: 10c0/c8ca9e0af97bddec56d33d0524eca075f1378112558cd0fbb242aaf36db7ccd0a172087ada555b78251bb29240c9ba31d04fd7798fc6ca723155cfbf670c1947 + languageName: node + linkType: hard + +"@tiptap/extension-typography@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/extension-typography@npm:2.5.4" + peerDependencies: + "@tiptap/core": ^2.5.4 + checksum: 10c0/3e288d4236ca15edf0128c5622ff2386df70aa73a3d34a71475ae6df9a739b9e46f3520b3a7ddc4c03aad93006206177df07f0a8491889c6bedb5bba9e8d87d4 + languageName: node + linkType: hard + +"@tiptap/pm@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/pm@npm:2.5.4" + dependencies: + prosemirror-changeset: "npm:^2.2.1" + prosemirror-collab: "npm:^1.3.1" + prosemirror-commands: "npm:^1.5.2" + prosemirror-dropcursor: "npm:^1.8.1" + prosemirror-gapcursor: "npm:^1.3.2" + prosemirror-history: "npm:^1.4.1" + prosemirror-inputrules: "npm:^1.4.0" + prosemirror-keymap: "npm:^1.2.2" + prosemirror-markdown: "npm:^1.13.0" + prosemirror-menu: "npm:^1.2.4" + prosemirror-model: "npm:^1.22.1" + prosemirror-schema-basic: "npm:^1.2.3" + prosemirror-schema-list: "npm:^1.4.1" + prosemirror-state: "npm:^1.4.3" + prosemirror-tables: "npm:^1.3.7" + prosemirror-trailing-node: "npm:^2.0.8" + prosemirror-transform: "npm:^1.9.0" + prosemirror-view: "npm:^1.33.8" + checksum: 10c0/bfc493ce951d823dfd8897c1233585fe1ef2293c5b3c9bcda78ddec6f6f18d413c7f3543c3a067a04a53dcf91d64cc1c198d7613219ec0fc1a4aa2d711ebc760 + languageName: node + linkType: hard + +"@tiptap/react@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/react@npm:2.5.4" + dependencies: + "@tiptap/extension-bubble-menu": "npm:^2.5.4" + "@tiptap/extension-floating-menu": "npm:^2.5.4" + "@types/use-sync-external-store": "npm:^0.0.6" + use-sync-external-store: "npm:^1.2.2" + peerDependencies: + "@tiptap/core": ^2.5.4 + "@tiptap/pm": ^2.5.4 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + checksum: 10c0/5aecf96620d195986beb24b0d04f5fd20f5a162279e5a55dde1190a9ff170251c59a499226cc32fb5d21733dd062fcae6352f52cd0555dcd09a92a379cbda0ad + languageName: node + linkType: hard + +"@tiptap/starter-kit@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/starter-kit@npm:2.5.4" + dependencies: + "@tiptap/core": "npm:^2.5.4" + "@tiptap/extension-blockquote": "npm:^2.5.4" + "@tiptap/extension-bold": "npm:^2.5.4" + "@tiptap/extension-bullet-list": "npm:^2.5.4" + "@tiptap/extension-code": "npm:^2.5.4" + "@tiptap/extension-code-block": "npm:^2.5.4" + "@tiptap/extension-document": "npm:^2.5.4" + "@tiptap/extension-dropcursor": "npm:^2.5.4" + "@tiptap/extension-gapcursor": "npm:^2.5.4" + "@tiptap/extension-hard-break": "npm:^2.5.4" + "@tiptap/extension-heading": "npm:^2.5.4" + "@tiptap/extension-history": "npm:^2.5.4" + "@tiptap/extension-horizontal-rule": "npm:^2.5.4" + "@tiptap/extension-italic": "npm:^2.5.4" + "@tiptap/extension-list-item": "npm:^2.5.4" + "@tiptap/extension-ordered-list": "npm:^2.5.4" + "@tiptap/extension-paragraph": "npm:^2.5.4" + "@tiptap/extension-strike": "npm:^2.5.4" + "@tiptap/extension-text": "npm:^2.5.4" + checksum: 10c0/95ad831788c1906d56d72431b73ad36a1642739800dbacc1889eeb85a17480aa6899c76fe1dd5aef91e5a673e85b36cc99f6ec4a9dbdeb023d2523ef91cc3045 + languageName: node + linkType: hard + +"@tiptap/suggestion@npm:^2.5.4": + version: 2.5.4 + resolution: "@tiptap/suggestion@npm:2.5.4" + peerDependencies: + "@tiptap/core": ^2.5.4 + "@tiptap/pm": ^2.5.4 + checksum: 10c0/d2f20fc9171d75de129b8341212f70f2a6f7f6003cca12a92fbf50c1c4ddfad663ef118105b6f4696770390ec9787d5389a3731870168441fe4ba8586c567cdf + languageName: node + linkType: hard + +"@trysound/sax@npm:0.2.0": + version: 0.2.0 + resolution: "@trysound/sax@npm:0.2.0" + checksum: 10c0/44907308549ce775a41c38a815f747009ac45929a45d642b836aa6b0a536e4978d30b8d7d680bbd116e9dd73b7dbe2ef0d1369dcfc2d09e83ba381e485ecbe12 + languageName: node + linkType: hard + +"@tsconfig/node10@npm:^1.0.7": + version: 1.0.11 + resolution: "@tsconfig/node10@npm:1.0.11" + checksum: 10c0/28a0710e5d039e0de484bdf85fee883bfd3f6a8980601f4d44066b0a6bcd821d31c4e231d1117731c4e24268bd4cf2a788a6787c12fc7f8d11014c07d582783c + languageName: node + linkType: hard + +"@tsconfig/node12@npm:^1.0.7": + version: 1.0.11 + resolution: "@tsconfig/node12@npm:1.0.11" + checksum: 10c0/dddca2b553e2bee1308a056705103fc8304e42bb2d2cbd797b84403a223b25c78f2c683ec3e24a095e82cd435387c877239bffcb15a590ba817cd3f6b9a99fd9 + languageName: node + linkType: hard + +"@tsconfig/node14@npm:^1.0.0": + version: 1.0.3 + resolution: "@tsconfig/node14@npm:1.0.3" + checksum: 10c0/67c1316d065fdaa32525bc9449ff82c197c4c19092b9663b23213c8cbbf8d88b6ed6a17898e0cbc2711950fbfaf40388938c1c748a2ee89f7234fc9e7fe2bf44 + languageName: node + linkType: hard + +"@tsconfig/node16@npm:^1.0.2": + version: 1.0.4 + resolution: "@tsconfig/node16@npm:1.0.4" + checksum: 10c0/05f8f2734e266fb1839eb1d57290df1664fe2aa3b0fdd685a9035806daa635f7519bf6d5d9b33f6e69dd545b8c46bd6e2b5c79acb2b1f146e885f7f11a42a5bb + languageName: node + linkType: hard + +"@tufjs/canonical-json@npm:2.0.0": + version: 2.0.0 + resolution: "@tufjs/canonical-json@npm:2.0.0" + checksum: 10c0/52c5ffaef1483ed5c3feedfeba26ca9142fa386eea54464e70ff515bd01c5e04eab05d01eff8c2593291dcaf2397ca7d9c512720e11f52072b04c47a5c279415 + languageName: node + linkType: hard + +"@tufjs/models@npm:2.0.1": + version: 2.0.1 + resolution: "@tufjs/models@npm:2.0.1" + dependencies: + "@tufjs/canonical-json": "npm:2.0.0" + minimatch: "npm:^9.0.4" + checksum: 10c0/ad9e82fd921954501fd90ed34ae062254637595577ad13fdc1e076405c0ea5ee7d8aebad09e63032972fd92b07f1786c15b24a195a171fc8ac470ca8e2ffbcc4 + languageName: node + linkType: hard + +"@tybys/wasm-util@npm:^0.9.0": + version: 0.9.0 + resolution: "@tybys/wasm-util@npm:0.9.0" + dependencies: + tslib: "npm:^2.4.0" + checksum: 10c0/f9fde5c554455019f33af6c8215f1a1435028803dc2a2825b077d812bed4209a1a64444a4ca0ce2ea7e1175c8d88e2f9173a36a33c199e8a5c671aa31de8242d + languageName: node + linkType: hard + +"@types/bcrypt@npm:^5.0.2": + version: 5.0.2 + resolution: "@types/bcrypt@npm:5.0.2" + dependencies: + "@types/node": "npm:*" + checksum: 10c0/dd7f05e183b9b1fc08ec499069febf197ab8e9c720766b5bbb5628395082e248f9a444c60882fe7788361fcadc302e21e055ab9c26a300f100e08791c353e6aa + languageName: node + linkType: hard + +"@types/body-parser@npm:*": + version: 1.19.5 + resolution: "@types/body-parser@npm:1.19.5" + dependencies: + "@types/connect": "npm:*" + "@types/node": "npm:*" + checksum: 10c0/aebeb200f25e8818d8cf39cd0209026750d77c9b85381cdd8deeb50913e4d18a1ebe4b74ca9b0b4d21952511eeaba5e9fbbf739b52731a2061e206ec60d568df + languageName: node + linkType: hard + +"@types/compression@npm:^1.7.5": + version: 1.7.5 + resolution: "@types/compression@npm:1.7.5" + dependencies: + "@types/express": "npm:*" + checksum: 10c0/3818f3d10cede38a835b40b80c341eae162aef1691f2e8f81178a77dbc109f04234cf760b6066eaa06ecbb1da143433c00db2fd9999198b76cd5a193e1d09675 + languageName: node + linkType: hard + +"@types/connect@npm:*": + version: 3.4.38 + resolution: "@types/connect@npm:3.4.38" + dependencies: + "@types/node": "npm:*" + checksum: 10c0/2e1cdba2c410f25649e77856505cd60223250fa12dff7a503e492208dbfdd25f62859918f28aba95315251fd1f5e1ffbfca1e25e73037189ab85dd3f8d0a148c + languageName: node + linkType: hard + +"@types/cookie-parser@npm:^1.4.7": + version: 1.4.7 + resolution: "@types/cookie-parser@npm:1.4.7" + dependencies: + "@types/express": "npm:*" + checksum: 10c0/af37fea5399950e59ceb2e1f25c633f3df360c4f17e8b3f26418e672fe5c926a20993b86f8e1df72cfe2c4dc8967d9a18d3d78b5c6a5f751a297d0418e5690fa + languageName: node + linkType: hard + +"@types/cors@npm:^2.8.17": + version: 2.8.17 + resolution: "@types/cors@npm:2.8.17" + dependencies: + "@types/node": "npm:*" + checksum: 10c0/457364c28c89f3d9ed34800e1de5c6eaaf344d1bb39af122f013322a50bc606eb2aa6f63de4e41a7a08ba7ef454473926c94a830636723da45bf786df032696d + languageName: node + linkType: hard + +"@types/d3-array@npm:^3.0.3": + version: 3.2.1 + resolution: "@types/d3-array@npm:3.2.1" + checksum: 10c0/38bf2c778451f4b79ec81a2288cb4312fe3d6449ecdf562970cc339b60f280f31c93a024c7ff512607795e79d3beb0cbda123bb07010167bce32927f71364bca + languageName: node + linkType: hard + +"@types/d3-color@npm:*": + version: 3.1.3 + resolution: "@types/d3-color@npm:3.1.3" + checksum: 10c0/65eb0487de606eb5ad81735a9a5b3142d30bc5ea801ed9b14b77cb14c9b909f718c059f13af341264ee189acf171508053342142bdf99338667cea26a2d8d6ae + languageName: node + linkType: hard + +"@types/d3-ease@npm:^3.0.0": + version: 3.0.2 + resolution: "@types/d3-ease@npm:3.0.2" + checksum: 10c0/aff5a1e572a937ee9bff6465225d7ba27d5e0c976bd9eacdac2e6f10700a7cb0c9ea2597aff6b43a6ed850a3210030870238894a77ec73e309b4a9d0333f099c + languageName: node + linkType: hard + +"@types/d3-interpolate@npm:^3.0.1": + version: 3.0.4 + resolution: "@types/d3-interpolate@npm:3.0.4" + dependencies: + "@types/d3-color": "npm:*" + checksum: 10c0/066ebb8da570b518dd332df6b12ae3b1eaa0a7f4f0c702e3c57f812cf529cc3500ec2aac8dc094f31897790346c6b1ebd8cd7a077176727f4860c2b181a65ca4 + languageName: node + linkType: hard + +"@types/d3-path@npm:*": + version: 3.1.0 + resolution: "@types/d3-path@npm:3.1.0" + checksum: 10c0/85e8b3aa968a60a5b33198ade06ae7ffedcf9a22d86f24859ff58e014b053ccb7141ec163b78d547bc8215bb12bb54171c666057ab6156912814005b686afb31 + languageName: node + linkType: hard + +"@types/d3-scale@npm:^4.0.2": + version: 4.0.8 + resolution: "@types/d3-scale@npm:4.0.8" + dependencies: + "@types/d3-time": "npm:*" + checksum: 10c0/57de90e4016f640b83cb960b7e3a0ab3ed02e720898840ddc5105264ffcfea73336161442fdc91895377c2d2f91904d637282f16852b8535b77e15a761c8e99e + languageName: node + linkType: hard + +"@types/d3-shape@npm:^3.1.0": + version: 3.1.6 + resolution: "@types/d3-shape@npm:3.1.6" + dependencies: + "@types/d3-path": "npm:*" + checksum: 10c0/0625715925d3c7ed3d44ce998b42c993f063c31605b6e4a8046c4be0fe724e2d214fc83e86d04f429a30a6e1f439053e92b0d9e59e1180c3a5327b4a6e79fa0a + languageName: node + linkType: hard + +"@types/d3-time@npm:*, @types/d3-time@npm:^3.0.0": + version: 3.0.3 + resolution: "@types/d3-time@npm:3.0.3" + checksum: 10c0/245a8aadca504df27edf730de502e47a68f16ae795c86b5ca35e7afa91c133aa9ef4d08778f8cf1ed2be732f89a4105ba4b437ce2afbdfd17d3d937b6ba5f568 + languageName: node + linkType: hard + +"@types/d3-timer@npm:^3.0.0": + version: 3.0.2 + resolution: "@types/d3-timer@npm:3.0.2" + checksum: 10c0/c644dd9571fcc62b1aa12c03bcad40571553020feeb5811f1d8a937ac1e65b8a04b759b4873aef610e28b8714ac71c9885a4d6c127a048d95118f7e5b506d9e1 + languageName: node + linkType: hard + +"@types/express-serve-static-core@npm:^4.17.33": + version: 4.19.5 + resolution: "@types/express-serve-static-core@npm:4.19.5" + dependencies: + "@types/node": "npm:*" + "@types/qs": "npm:*" + "@types/range-parser": "npm:*" + "@types/send": "npm:*" + checksum: 10c0/ba8d8d976ab797b2602c60e728802ff0c98a00f13d420d82770f3661b67fa36ea9d3be0b94f2ddd632afe1fbc6e41620008b01db7e4fabdd71a2beb5539b0725 + languageName: node + linkType: hard + +"@types/express@npm:*, @types/express@npm:^4.17.21": + version: 4.17.21 + resolution: "@types/express@npm:4.17.21" + dependencies: + "@types/body-parser": "npm:*" + "@types/express-serve-static-core": "npm:^4.17.33" + "@types/qs": "npm:*" + "@types/serve-static": "npm:*" + checksum: 10c0/12e562c4571da50c7d239e117e688dc434db1bac8be55613294762f84fd77fbd0658ccd553c7d3ab02408f385bc93980992369dd30e2ecd2c68c358e6af8fabf + languageName: node + linkType: hard + +"@types/hast@npm:^2.0.0": + version: 2.3.10 + resolution: "@types/hast@npm:2.3.10" + dependencies: + "@types/unist": "npm:^2" + checksum: 10c0/16daac35d032e656defe1f103f9c09c341a6dc553c7ec17b388274076fa26e904a71ea5ea41fd368a6d5f1e9e53be275c80af7942b9c466d8511d261c9529c7e + languageName: node + linkType: hard + +"@types/http-errors@npm:*": + version: 2.0.4 + resolution: "@types/http-errors@npm:2.0.4" + checksum: 10c0/494670a57ad4062fee6c575047ad5782506dd35a6b9ed3894cea65830a94367bd84ba302eb3dde331871f6d70ca287bfedb1b2cf658e6132cd2cbd427ab56836 + languageName: node + linkType: hard + +"@types/ioredis@npm:^5.0.0": + version: 5.0.0 + resolution: "@types/ioredis@npm:5.0.0" + dependencies: + ioredis: "npm:*" + checksum: 10c0/e52ce4239f0334701fc95fb5aaf1753d75f7582099fdf152743192f49d9ee4a88478b339d015e50cb5e111e38925846cf20668355f4046af7855021d2be181f0 + languageName: node + linkType: hard + +"@types/jsonwebtoken@npm:^9.0.6": + version: 9.0.6 + resolution: "@types/jsonwebtoken@npm:9.0.6" + dependencies: + "@types/node": "npm:*" + checksum: 10c0/9c29e3896e5fb6056e54d87514643e59e0cfb966ae25171a107776270195bba955f0373e98c8ed6450c145b18984f5df9cf0fcac360f382cec3c7c4d3510b202 + languageName: node + linkType: hard + +"@types/mime@npm:^1": + version: 1.3.5 + resolution: "@types/mime@npm:1.3.5" + checksum: 10c0/c2ee31cd9b993804df33a694d5aa3fa536511a49f2e06eeab0b484fef59b4483777dbb9e42a4198a0809ffbf698081fdbca1e5c2218b82b91603dfab10a10fbc + languageName: node + linkType: hard + +"@types/minimatch@npm:^3.0.3": + version: 3.0.5 + resolution: "@types/minimatch@npm:3.0.5" + checksum: 10c0/a1a19ba342d6f39b569510f621ae4bbe972dc9378d15e9a5e47904c440ee60744f5b09225bc73be1c6490e3a9c938eee69eb53debf55ce1f15761201aa965f97 + languageName: node + linkType: hard + +"@types/minimist@npm:^1.2.0": + version: 1.2.5 + resolution: "@types/minimist@npm:1.2.5" + checksum: 10c0/3f791258d8e99a1d7d0ca2bda1ca6ea5a94e5e7b8fc6cde84dd79b0552da6fb68ade750f0e17718f6587783c24254bbca0357648dd59dc3812c150305cabdc46 + languageName: node + linkType: hard + +"@types/mjml-core@npm:*": + version: 4.15.0 + resolution: "@types/mjml-core@npm:4.15.0" + checksum: 10c0/8e99b8da13952518dc62aae9b1c8a5b8a18fdc10645412dea12282e70384da23c21acdc800cbda091819acb9eb3aad30ba45f728057ab08cbd4eff260a46def6 + languageName: node + linkType: hard + +"@types/mjml@npm:^4.7.4": + version: 4.7.4 + resolution: "@types/mjml@npm:4.7.4" + dependencies: + "@types/mjml-core": "npm:*" + checksum: 10c0/6f4bbdf709e1f6c9b26be67146b1e4c759142fb4ddfa4c079b600835701bb2039c60cd530d016f1d2f1aef4256580e30cdc942c3acd6935e2fe56b5a665795ae + languageName: node + linkType: hard + +"@types/morgan@npm:^1.9.9": + version: 1.9.9 + resolution: "@types/morgan@npm:1.9.9" + dependencies: + "@types/node": "npm:*" + checksum: 10c0/2b310c6f7c3cd1d0e19fd8c644c6f4edd58415bb88be78cc8bea853c02b165a76e6d7ade91f5b92c124e9b9cf3a810c23e400392d5e30118988f68575dcd1ed4 + languageName: node + linkType: hard + +"@types/multer@npm:^1.4.11": + version: 1.4.11 + resolution: "@types/multer@npm:1.4.11" + dependencies: + "@types/express": "npm:*" + checksum: 10c0/ace8e9f5ac7d2d7f6e0c35b790504f582a2f82a84cc06a7b90315527599b95256595bc0bb5bba60220c20a558554f0c21b96b94848b885987ab69512a3a9865e + languageName: node + linkType: hard + +"@types/node-cron@npm:^3.0.11": + version: 3.0.11 + resolution: "@types/node-cron@npm:3.0.11" + checksum: 10c0/21cd0255988da2935bc8bf010da8b31624911ec10ef5d9a96a59c57193f60b27c1749b2e27c2a292a697d00195da5b8848a707345bb276abc39c96287ee82005 + languageName: node + linkType: hard + +"@types/node@npm:*, @types/node@npm:20.14.11": + version: 20.14.11 + resolution: "@types/node@npm:20.14.11" + dependencies: + undici-types: "npm:~5.26.4" + checksum: 10c0/5306becc0ff41d81b1e31524bd376e958d0741d1ce892dffd586b9ae0cb6553c62b0d62abd16da8bea6b9a2c17572d360450535d7c073794b0cef9cb4e39691e + languageName: node + linkType: hard + +"@types/normalize-package-data@npm:^2.4.0": + version: 2.4.4 + resolution: "@types/normalize-package-data@npm:2.4.4" + checksum: 10c0/aef7bb9b015883d6f4119c423dd28c4bdc17b0e8a0ccf112c78b4fe0e91fbc4af7c6204b04bba0e199a57d2f3fbbd5b4a14bf8739bf9d2a39b2a0aad545e0f86 + languageName: node + linkType: hard + +"@types/nprogress@npm:^0.2.3": + version: 0.2.3 + resolution: "@types/nprogress@npm:0.2.3" + checksum: 10c0/cac0fe73aca79bc1472a1556f56303df72026f2fcd8dbe311fe84b1f46e454b9040c1e69223d94e3bf156a5986382170c52ebda19aa70f555f1b6855d8f744a6 + languageName: node + linkType: hard + +"@types/prop-types@npm:*": + version: 15.7.12 + resolution: "@types/prop-types@npm:15.7.12" + checksum: 10c0/1babcc7db6a1177779f8fde0ccc78d64d459906e6ef69a4ed4dd6339c920c2e05b074ee5a92120fe4e9d9f1a01c952f843ebd550bee2332fc2ef81d1706878f8 + languageName: node + linkType: hard + +"@types/qs@npm:*": + version: 6.9.15 + resolution: "@types/qs@npm:6.9.15" + checksum: 10c0/49c5ff75ca3adb18a1939310042d273c9fc55920861bd8e5100c8a923b3cda90d759e1a95e18334092da1c8f7b820084687770c83a1ccef04fb2c6908117c823 + languageName: node + linkType: hard + +"@types/range-parser@npm:*": + version: 1.2.7 + resolution: "@types/range-parser@npm:1.2.7" + checksum: 10c0/361bb3e964ec5133fa40644a0b942279ed5df1949f21321d77de79f48b728d39253e5ce0408c9c17e4e0fd95ca7899da36841686393b9f7a1e209916e9381a3c + languageName: node + linkType: hard + +"@types/react-syntax-highlighter@npm:^15.5.13": + version: 15.5.13 + resolution: "@types/react-syntax-highlighter@npm:15.5.13" + dependencies: + "@types/react": "npm:*" + checksum: 10c0/e3bca325b27519fb063d3370de20d311c188ec16ffc01e5bc77bdf2d7320756725ee3d0246922cd5d38b75c5065a1bc43d0194e92ecf6556818714b4ffb0967a + languageName: node + linkType: hard + +"@types/react@npm:*, @types/react@npm:18.3.3": + version: 18.3.3 + resolution: "@types/react@npm:18.3.3" + dependencies: + "@types/prop-types": "npm:*" + csstype: "npm:^3.0.2" + checksum: 10c0/fe455f805c5da13b89964c3d68060cebd43e73ec15001a68b34634604a78140e6fc202f3f61679b9d809dde6d7a7c2cb3ed51e0fd1462557911db09879b55114 + languageName: node + linkType: hard + +"@types/send@npm:*": + version: 0.17.4 + resolution: "@types/send@npm:0.17.4" + dependencies: + "@types/mime": "npm:^1" + "@types/node": "npm:*" + checksum: 10c0/7f17fa696cb83be0a104b04b424fdedc7eaba1c9a34b06027239aba513b398a0e2b7279778af521f516a397ced417c96960e5f50fcfce40c4bc4509fb1a5883c + languageName: node + linkType: hard + +"@types/serve-static@npm:*": + version: 1.15.7 + resolution: "@types/serve-static@npm:1.15.7" + dependencies: + "@types/http-errors": "npm:*" + "@types/node": "npm:*" + "@types/send": "npm:*" + checksum: 10c0/26ec864d3a626ea627f8b09c122b623499d2221bbf2f470127f4c9ebfe92bd8a6bb5157001372d4c4bd0dd37a1691620217d9dc4df5aa8f779f3fd996b1c60ae + languageName: node + linkType: hard + +"@types/signale@npm:^1.4.7": + version: 1.4.7 + resolution: "@types/signale@npm:1.4.7" + dependencies: + "@types/node": "npm:*" + checksum: 10c0/deff3ccc072c7cdb5b97242799c45a5f477ee20a1150b0798e8e88ecb0cd266466ef25a535892ce672cb7ea7904024a1d58ab7492e58df5854ad519c4f72e518 + languageName: node + linkType: hard + +"@types/strip-bom@npm:^3.0.0": + version: 3.0.0 + resolution: "@types/strip-bom@npm:3.0.0" + checksum: 10c0/6638635fb52dc1f7a4aa596445170ffc731f3bea307d25d79709dcce14f80870128a6f0304032863b9d1a86b4b5f45d48bcaf96abe81f42e61f0a3eb18a1b996 + languageName: node + linkType: hard + +"@types/strip-json-comments@npm:0.0.30": + version: 0.0.30 + resolution: "@types/strip-json-comments@npm:0.0.30" + checksum: 10c0/90509e345ac16c79f7aa7d7ef52e388e5be923f3456cf8052d36ee0eb4abc5ec4080c5f010f78cf01f5599546577eb3724256bc698663e86f0fe08a5a3fb7f68 + languageName: node + linkType: hard + +"@types/unist@npm:^2": + version: 2.0.10 + resolution: "@types/unist@npm:2.0.10" + checksum: 10c0/5f247dc2229944355209ad5c8e83cfe29419fa7f0a6d557421b1985a1500444719cc9efcc42c652b55aab63c931813c88033e0202c1ac684bcd4829d66e44731 + languageName: node + linkType: hard + +"@types/use-sync-external-store@npm:^0.0.6": + version: 0.0.6 + resolution: "@types/use-sync-external-store@npm:0.0.6" + checksum: 10c0/77c045a98f57488201f678b181cccd042279aff3da34540ad242f893acc52b358bd0a8207a321b8ac09adbcef36e3236944390e2df4fcedb556ce7bb2a88f2a8 + languageName: node + linkType: hard + +"@uiball/loaders@npm:^1.3.1": + version: 1.3.1 + resolution: "@uiball/loaders@npm:1.3.1" + peerDependencies: + react: ">=16.8.0" + react-dom: ">=16.8.0" + checksum: 10c0/c12bd86c23a1d49ea40589ba48fc00a09edc5061a808be4cb32931e437acb4aded42161b4092a1b48e8e1fd3b867df213c03f193f8828d612f0f247c956956d8 + languageName: node + linkType: hard + +"@yarnpkg/lockfile@npm:^1.1.0": + version: 1.1.0 + resolution: "@yarnpkg/lockfile@npm:1.1.0" + checksum: 10c0/0bfa50a3d756623d1f3409bc23f225a1d069424dbc77c6fd2f14fb377390cd57ec703dc70286e081c564be9051ead9ba85d81d66a3e68eeb6eb506d4e0c0fbda + languageName: node + linkType: hard + +"@yarnpkg/parsers@npm:3.0.0-rc.46": + version: 3.0.0-rc.46 + resolution: "@yarnpkg/parsers@npm:3.0.0-rc.46" + dependencies: + js-yaml: "npm:^3.10.0" + tslib: "npm:^2.4.0" + checksum: 10c0/c7f421c6885142f351459031c093fb2e79abcce6f4a89765a10e600bb7ab122949c54bcea2b23de9572a2b34ba29f822b17831c1c43ba50373ceb8cb5b336667 + languageName: node + linkType: hard + +"@zkochan/js-yaml@npm:0.0.7": + version: 0.0.7 + resolution: "@zkochan/js-yaml@npm:0.0.7" + dependencies: + argparse: "npm:^2.0.1" + bin: + js-yaml: bin/js-yaml.js + checksum: 10c0/c8b3525717912811f9422ed50e94c5751ed6f771eb1b7e5cde097f14835654931e2bdaecb1e5fc37b51cf8d822410a307f16dd1581d46149398c30215f3f9bac + languageName: node + linkType: hard + +"JSONStream@npm:^1.3.5": + version: 1.3.5 + resolution: "JSONStream@npm:1.3.5" + dependencies: + jsonparse: "npm:^1.2.0" + through: "npm:>=2.2.7 <3" + bin: + JSONStream: ./bin.js + checksum: 10c0/0f54694da32224d57b715385d4a6b668d2117379d1f3223dc758459246cca58fdc4c628b83e8a8883334e454a0a30aa198ede77c788b55537c1844f686a751f2 + languageName: node + linkType: hard + +"abbrev@npm:1": + version: 1.1.1 + resolution: "abbrev@npm:1.1.1" + checksum: 10c0/3f762677702acb24f65e813070e306c61fafe25d4b2583f9dfc935131f774863f3addd5741572ed576bd69cabe473c5af18e1e108b829cb7b6b4747884f726e6 + languageName: node + linkType: hard + +"abbrev@npm:^2.0.0": + version: 2.0.0 + resolution: "abbrev@npm:2.0.0" + checksum: 10c0/f742a5a107473946f426c691c08daba61a1d15942616f300b5d32fd735be88fef5cba24201757b6c407fd564555fb48c751cfa33519b2605c8a7aadd22baf372 + languageName: node + linkType: hard + +"accepts@npm:~1.3.5, accepts@npm:~1.3.8": + version: 1.3.8 + resolution: "accepts@npm:1.3.8" + dependencies: + mime-types: "npm:~2.1.34" + negotiator: "npm:0.6.3" + checksum: 10c0/3a35c5f5586cfb9a21163ca47a5f77ac34fa8ceb5d17d2fa2c0d81f41cbd7f8c6fa52c77e2c039acc0f4d09e71abdc51144246900f6bef5e3c4b333f77d89362 + languageName: node + linkType: hard + +"acorn-walk@npm:^8.1.1": + version: 8.3.3 + resolution: "acorn-walk@npm:8.3.3" + dependencies: + acorn: "npm:^8.11.0" + checksum: 10c0/4a9e24313e6a0a7b389e712ba69b66b455b4cb25988903506a8d247e7b126f02060b05a8a5b738a9284214e4ca95f383dd93443a4ba84f1af9b528305c7f243b + languageName: node + linkType: hard + +"acorn@npm:^8.11.0, acorn@npm:^8.4.1": + version: 8.12.1 + resolution: "acorn@npm:8.12.1" + bin: + acorn: bin/acorn + checksum: 10c0/51fb26cd678f914e13287e886da2d7021f8c2bc0ccc95e03d3e0447ee278dd3b40b9c57dc222acd5881adcf26f3edc40901a4953403232129e3876793cd17386 + languageName: node + linkType: hard + +"add-stream@npm:^1.0.0": + version: 1.0.0 + resolution: "add-stream@npm:1.0.0" + checksum: 10c0/985014a14e76ca4cb24e0fc58bb1556794cf38c5c8937de335a10584f50a371dc48e1c34a59391c7eb9c1fc908b4b86764df5d2756f701df6ba95d1ca2f63ddc + languageName: node + linkType: hard + +"agent-base@npm:6": + version: 6.0.2 + resolution: "agent-base@npm:6.0.2" + dependencies: + debug: "npm:4" + checksum: 10c0/dc4f757e40b5f3e3d674bc9beb4f1048f4ee83af189bae39be99f57bf1f48dde166a8b0a5342a84b5944ee8e6ed1e5a9d801858f4ad44764e84957122fe46261 + languageName: node + linkType: hard + +"agent-base@npm:^7.0.2, agent-base@npm:^7.1.0, agent-base@npm:^7.1.1": + version: 7.1.1 + resolution: "agent-base@npm:7.1.1" + dependencies: + debug: "npm:^4.3.4" + checksum: 10c0/e59ce7bed9c63bf071a30cc471f2933862044c97fd9958967bfe22521d7a0f601ce4ed5a8c011799d0c726ca70312142ae193bbebb60f576b52be19d4a363b50 + languageName: node + linkType: hard + +"aggregate-error@npm:^3.0.0": + version: 3.1.0 + resolution: "aggregate-error@npm:3.1.0" + dependencies: + clean-stack: "npm:^2.0.0" + indent-string: "npm:^4.0.0" + checksum: 10c0/a42f67faa79e3e6687a4923050e7c9807db3848a037076f791d10e092677d65c1d2d863b7848560699f40fc0502c19f40963fb1cd1fb3d338a7423df8e45e039 + languageName: node + linkType: hard + +"ansi-colors@npm:^4.1.1": + version: 4.1.3 + resolution: "ansi-colors@npm:4.1.3" + checksum: 10c0/ec87a2f59902f74e61eada7f6e6fe20094a628dab765cfdbd03c3477599368768cffccdb5d3bb19a1b6c99126783a143b1fee31aab729b31ffe5836c7e5e28b9 + languageName: node + linkType: hard + +"ansi-escapes@npm:^4.2.1": + version: 4.3.2 + resolution: "ansi-escapes@npm:4.3.2" + dependencies: + type-fest: "npm:^0.21.3" + checksum: 10c0/da917be01871525a3dfcf925ae2977bc59e8c513d4423368645634bf5d4ceba5401574eb705c1e92b79f7292af5a656f78c5725a4b0e1cec97c4b413705c1d50 + languageName: node + linkType: hard + +"ansi-regex@npm:^5.0.1": + version: 5.0.1 + resolution: "ansi-regex@npm:5.0.1" + checksum: 10c0/9a64bb8627b434ba9327b60c027742e5d17ac69277960d041898596271d992d4d52ba7267a63ca10232e29f6107fc8a835f6ce8d719b88c5f8493f8254813737 + languageName: node + linkType: hard + +"ansi-regex@npm:^6.0.1": + version: 6.0.1 + resolution: "ansi-regex@npm:6.0.1" + checksum: 10c0/cbe16dbd2c6b2735d1df7976a7070dd277326434f0212f43abf6d87674095d247968209babdaad31bb00882fa68807256ba9be340eec2f1004de14ca75f52a08 + languageName: node + linkType: hard + +"ansi-styles@npm:^3.2.1": + version: 3.2.1 + resolution: "ansi-styles@npm:3.2.1" + dependencies: + color-convert: "npm:^1.9.0" + checksum: 10c0/ece5a8ef069fcc5298f67e3f4771a663129abd174ea2dfa87923a2be2abf6cd367ef72ac87942da00ce85bd1d651d4cd8595aebdb1b385889b89b205860e977b + languageName: node + linkType: hard + +"ansi-styles@npm:^4.0.0, ansi-styles@npm:^4.1.0": + version: 4.3.0 + resolution: "ansi-styles@npm:4.3.0" + dependencies: + color-convert: "npm:^2.0.1" + checksum: 10c0/895a23929da416f2bd3de7e9cb4eabd340949328ab85ddd6e484a637d8f6820d485f53933446f5291c3b760cbc488beb8e88573dd0f9c7daf83dccc8fe81b041 + languageName: node + linkType: hard + +"ansi-styles@npm:^5.0.0": + version: 5.2.0 + resolution: "ansi-styles@npm:5.2.0" + checksum: 10c0/9c4ca80eb3c2fb7b33841c210d2f20807f40865d27008d7c3f707b7f95cab7d67462a565e2388ac3285b71cb3d9bb2173de8da37c57692a362885ec34d6e27df + languageName: node + linkType: hard + +"ansi-styles@npm:^6.1.0": + version: 6.2.1 + resolution: "ansi-styles@npm:6.2.1" + checksum: 10c0/5d1ec38c123984bcedd996eac680d548f31828bd679a66db2bdf11844634dde55fec3efa9c6bb1d89056a5e79c1ac540c4c784d592ea1d25028a92227d2f2d5c + languageName: node + linkType: hard + +"any-promise@npm:^1.0.0": + version: 1.3.0 + resolution: "any-promise@npm:1.3.0" + checksum: 10c0/60f0298ed34c74fef50daab88e8dab786036ed5a7fad02e012ab57e376e0a0b4b29e83b95ea9b5e7d89df762f5f25119b83e00706ecaccb22cfbacee98d74889 + languageName: node + linkType: hard + +"anymatch@npm:~3.1.2": + version: 3.1.3 + resolution: "anymatch@npm:3.1.3" + dependencies: + normalize-path: "npm:^3.0.0" + picomatch: "npm:^2.0.4" + checksum: 10c0/57b06ae984bc32a0d22592c87384cd88fe4511b1dd7581497831c56d41939c8a001b28e7b853e1450f2bf61992dfcaa8ae2d0d161a0a90c4fb631ef07098fbac + languageName: node + linkType: hard + +"append-field@npm:^1.0.0": + version: 1.0.0 + resolution: "append-field@npm:1.0.0" + checksum: 10c0/1b5abcc227e5179936a9e4f7e2af4769fa1f00eda85bbaed907f7964b0fd1f7d61f0f332b35337f391389ff13dd5310c2546ba670f8e5a743b23ec85185c73ef + languageName: node + linkType: hard + +"aproba@npm:2.0.0, aproba@npm:^1.0.3 || ^2.0.0": + version: 2.0.0 + resolution: "aproba@npm:2.0.0" + checksum: 10c0/d06e26384a8f6245d8c8896e138c0388824e259a329e0c9f196b4fa533c82502a6fd449586e3604950a0c42921832a458bb3aa0aa9f0ba449cfd4f50fd0d09b5 + languageName: node + linkType: hard + +"are-we-there-yet@npm:^2.0.0": + version: 2.0.0 + resolution: "are-we-there-yet@npm:2.0.0" + dependencies: + delegates: "npm:^1.0.0" + readable-stream: "npm:^3.6.0" + checksum: 10c0/375f753c10329153c8d66dc95e8f8b6c7cc2aa66e05cb0960bd69092b10dae22900cacc7d653ad11d26b3ecbdbfe1e8bfb6ccf0265ba8077a7d979970f16b99c + languageName: node + linkType: hard + +"arg@npm:^4.1.0": + version: 4.1.3 + resolution: "arg@npm:4.1.3" + checksum: 10c0/070ff801a9d236a6caa647507bdcc7034530604844d64408149a26b9e87c2f97650055c0f049abd1efc024b334635c01f29e0b632b371ac3f26130f4cf65997a + languageName: node + linkType: hard + +"arg@npm:^5.0.2": + version: 5.0.2 + resolution: "arg@npm:5.0.2" + checksum: 10c0/ccaf86f4e05d342af6666c569f844bec426595c567d32a8289715087825c2ca7edd8a3d204e4d2fb2aa4602e09a57d0c13ea8c9eea75aac3dbb4af5514e6800e + languageName: node + linkType: hard + +"argparse@npm:^1.0.7": + version: 1.0.10 + resolution: "argparse@npm:1.0.10" + dependencies: + sprintf-js: "npm:~1.0.2" + checksum: 10c0/b2972c5c23c63df66bca144dbc65d180efa74f25f8fd9b7d9a0a6c88ae839db32df3d54770dcb6460cf840d232b60695d1a6b1053f599d84e73f7437087712de + languageName: node + linkType: hard + +"argparse@npm:^2.0.1": + version: 2.0.1 + resolution: "argparse@npm:2.0.1" + checksum: 10c0/c5640c2d89045371c7cedd6a70212a04e360fd34d6edeae32f6952c63949e3525ea77dbec0289d8213a99bbaeab5abfa860b5c12cf88a2e6cf8106e90dd27a7e + languageName: node + linkType: hard + +"array-differ@npm:^3.0.0": + version: 3.0.0 + resolution: "array-differ@npm:3.0.0" + checksum: 10c0/c0d924cc2b7e3f5a0e6ae932e8941c5fddc0412bcecf8d5152641910e60f5e1c1e87da2b32083dec2f92f9a8f78e916ea68c22a0579794ba49886951ae783123 + languageName: node + linkType: hard + +"array-flatten@npm:1.1.1": + version: 1.1.1 + resolution: "array-flatten@npm:1.1.1" + checksum: 10c0/806966c8abb2f858b08f5324d9d18d7737480610f3bd5d3498aaae6eb5efdc501a884ba019c9b4a8f02ff67002058749d05548fd42fa8643f02c9c7f22198b91 + languageName: node + linkType: hard + +"array-ify@npm:^1.0.0": + version: 1.0.0 + resolution: "array-ify@npm:1.0.0" + checksum: 10c0/75c9c072faac47bd61779c0c595e912fe660d338504ac70d10e39e1b8a4a0c9c87658703d619b9d1b70d324177ae29dc8d07dda0d0a15d005597bc4c5a59c70c + languageName: node + linkType: hard + +"array-union@npm:^2.1.0": + version: 2.1.0 + resolution: "array-union@npm:2.1.0" + checksum: 10c0/429897e68110374f39b771ec47a7161fc6a8fc33e196857c0a396dc75df0b5f65e4d046674db764330b6bb66b39ef48dd7c53b6a2ee75cfb0681e0c1a7033962 + languageName: node + linkType: hard + +"arrify@npm:^1.0.1": + version: 1.0.1 + resolution: "arrify@npm:1.0.1" + checksum: 10c0/c35c8d1a81bcd5474c0c57fe3f4bad1a4d46a5fa353cedcff7a54da315df60db71829e69104b859dff96c5d68af46bd2be259fe5e50dc6aa9df3b36bea0383ab + languageName: node + linkType: hard + +"arrify@npm:^2.0.1": + version: 2.0.1 + resolution: "arrify@npm:2.0.1" + checksum: 10c0/3fb30b5e7c37abea1907a60b28a554d2f0fc088757ca9bf5b684786e583fdf14360721eb12575c1ce6f995282eab936712d3c4389122682eafab0e0b57f78dbb + languageName: node + linkType: hard + +"async@npm:^3.2.3": + version: 3.2.5 + resolution: "async@npm:3.2.5" + checksum: 10c0/1408287b26c6db67d45cb346e34892cee555b8b59e6c68e6f8c3e495cad5ca13b4f218180e871f3c2ca30df4ab52693b66f2f6ff43644760cab0b2198bda79c1 + languageName: node + linkType: hard + +"asynckit@npm:^0.4.0": + version: 0.4.0 + resolution: "asynckit@npm:0.4.0" + checksum: 10c0/d73e2ddf20c4eb9337e1b3df1a0f6159481050a5de457c55b14ea2e5cb6d90bb69e004c9af54737a5ee0917fcf2c9e25de67777bbe58261847846066ba75bc9d + languageName: node + linkType: hard + +"autoprefixer@npm:^10.4.19": + version: 10.4.19 + resolution: "autoprefixer@npm:10.4.19" + dependencies: + browserslist: "npm:^4.23.0" + caniuse-lite: "npm:^1.0.30001599" + fraction.js: "npm:^4.3.7" + normalize-range: "npm:^0.1.2" + picocolors: "npm:^1.0.0" + postcss-value-parser: "npm:^4.2.0" + peerDependencies: + postcss: ^8.1.0 + bin: + autoprefixer: bin/autoprefixer + checksum: 10c0/fe0178eb8b1da4f15c6535cd329926609b22d1811e047371dccce50563623f8075dd06fb167daff059e4228da651b0bdff6d9b44281541eaf0ce0b79125bfd19 + languageName: node + linkType: hard + +"axios@npm:^1.6.0": + version: 1.7.2 + resolution: "axios@npm:1.7.2" + dependencies: + follow-redirects: "npm:^1.15.6" + form-data: "npm:^4.0.0" + proxy-from-env: "npm:^1.1.0" + checksum: 10c0/cbd47ce380fe045313364e740bb03b936420b8b5558c7ea36a4563db1258c658f05e40feb5ddd41f6633fdd96d37ac2a76f884dad599c5b0224b4c451b3fa7ae + languageName: node + linkType: hard + +"babel-plugin-polyfill-corejs2@npm:^0.4.10": + version: 0.4.11 + resolution: "babel-plugin-polyfill-corejs2@npm:0.4.11" + dependencies: + "@babel/compat-data": "npm:^7.22.6" + "@babel/helper-define-polyfill-provider": "npm:^0.6.2" + semver: "npm:^6.3.1" + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 10c0/b2217bc8d5976cf8142453ed44daabf0b2e0e75518f24eac83b54a8892e87a88f1bd9089daa92fd25df979ecd0acfd29b6bc28c4182c1c46344cee15ef9bce84 + languageName: node + linkType: hard + +"babel-plugin-polyfill-corejs3@npm:^0.10.4": + version: 0.10.4 + resolution: "babel-plugin-polyfill-corejs3@npm:0.10.4" + dependencies: + "@babel/helper-define-polyfill-provider": "npm:^0.6.1" + core-js-compat: "npm:^3.36.1" + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 10c0/31b92cd3dfb5b417da8dfcf0deaa4b8b032b476d7bb31ca51c66127cf25d41e89260e89d17bc004b2520faa38aa9515fafabf81d89f9d4976e9dc1163e4a7c41 + languageName: node + linkType: hard + +"babel-plugin-polyfill-regenerator@npm:^0.6.1": + version: 0.6.2 + resolution: "babel-plugin-polyfill-regenerator@npm:0.6.2" + dependencies: + "@babel/helper-define-polyfill-provider": "npm:^0.6.2" + peerDependencies: + "@babel/core": ^7.4.0 || ^8.0.0-0 <8.0.0 + checksum: 10c0/bc541037cf7620bc84ddb75a1c0ce3288f90e7d2799c070a53f8a495c8c8ae0316447becb06f958dd25dcce2a2fce855d318ecfa48036a1ddb218d55aa38a744 + languageName: node + linkType: hard + +"balanced-match@npm:^1.0.0": + version: 1.0.2 + resolution: "balanced-match@npm:1.0.2" + checksum: 10c0/9308baf0a7e4838a82bbfd11e01b1cb0f0cf2893bc1676c27c2a8c0e70cbae1c59120c3268517a8ae7fb6376b4639ef81ca22582611dbee4ed28df945134aaee + languageName: node + linkType: hard + +"base64-js@npm:^1.3.1": + version: 1.5.1 + resolution: "base64-js@npm:1.5.1" + checksum: 10c0/f23823513b63173a001030fae4f2dabe283b99a9d324ade3ad3d148e218134676f1ee8568c877cd79ec1c53158dcf2d2ba527a97c606618928ba99dd930102bf + languageName: node + linkType: hard + +"basic-auth@npm:~2.0.1": + version: 2.0.1 + resolution: "basic-auth@npm:2.0.1" + dependencies: + safe-buffer: "npm:5.1.2" + checksum: 10c0/05f56db3a0fc31c89c86b605231e32ee143fb6ae38dc60616bc0970ae6a0f034172def99e69d3aed0e2c9e7cac84e2d63bc51a0b5ff6ab5fc8808cc8b29923c1 + languageName: node + linkType: hard + +"bcrypt@npm:^5.1.1": + version: 5.1.1 + resolution: "bcrypt@npm:5.1.1" + dependencies: + "@mapbox/node-pre-gyp": "npm:^1.0.11" + node-addon-api: "npm:^5.0.0" + checksum: 10c0/743231158c866bddc46f25eb8e9617fe38bc1a6f5f3052aba35e361d349b7f8fb80e96b45c48a4c23c45c29967ccd11c81cf31166454fc0ab019801c336cab40 + languageName: node + linkType: hard + +"before-after-hook@npm:^2.2.0": + version: 2.2.3 + resolution: "before-after-hook@npm:2.2.3" + checksum: 10c0/0488c4ae12df758ca9d49b3bb27b47fd559677965c52cae7b335784724fb8bf96c42b6e5ba7d7afcbc31facb0e294c3ef717cc41c5bc2f7bd9e76f8b90acd31c + languageName: node + linkType: hard + +"bin-links@npm:^4.0.4": + version: 4.0.4 + resolution: "bin-links@npm:4.0.4" + dependencies: + cmd-shim: "npm:^6.0.0" + npm-normalize-package-bin: "npm:^3.0.0" + read-cmd-shim: "npm:^4.0.0" + write-file-atomic: "npm:^5.0.0" + checksum: 10c0/feb664e786429289d189c19c193b28d855c2898bc53b8391306cbad2273b59ccecb91fd31a433020019552c3bad3a1e0eeecca1c12e739a12ce2ca94f7553a17 + languageName: node + linkType: hard + +"binary-extensions@npm:^2.0.0": + version: 2.3.0 + resolution: "binary-extensions@npm:2.3.0" + checksum: 10c0/75a59cafc10fb12a11d510e77110c6c7ae3f4ca22463d52487709ca7f18f69d886aa387557cc9864fbdb10153d0bdb4caacabf11541f55e89ed6e18d12ece2b5 + languageName: node + linkType: hard + +"bl@npm:^4.0.3, bl@npm:^4.1.0": + version: 4.1.0 + resolution: "bl@npm:4.1.0" + dependencies: + buffer: "npm:^5.5.0" + inherits: "npm:^2.0.4" + readable-stream: "npm:^3.4.0" + checksum: 10c0/02847e1d2cb089c9dc6958add42e3cdeaf07d13f575973963335ac0fdece563a50ac770ac4c8fa06492d2dd276f6cc3b7f08c7cd9c7a7ad0f8d388b2a28def5f + languageName: node + linkType: hard + +"body-parser@npm:1.20.2, body-parser@npm:^1.20.2": + version: 1.20.2 + resolution: "body-parser@npm:1.20.2" + dependencies: + bytes: "npm:3.1.2" + content-type: "npm:~1.0.5" + debug: "npm:2.6.9" + depd: "npm:2.0.0" + destroy: "npm:1.2.0" + http-errors: "npm:2.0.0" + iconv-lite: "npm:0.4.24" + on-finished: "npm:2.4.1" + qs: "npm:6.11.0" + raw-body: "npm:2.5.2" + type-is: "npm:~1.6.18" + unpipe: "npm:1.0.0" + checksum: 10c0/06f1438fff388a2e2354c96aa3ea8147b79bfcb1262dfcc2aae68ec13723d01d5781680657b74e9f83c808266d5baf52804032fbde2b7382b89bd8cdb273ace9 + languageName: node + linkType: hard + +"boolbase@npm:^1.0.0": + version: 1.0.0 + resolution: "boolbase@npm:1.0.0" + checksum: 10c0/e4b53deb4f2b85c52be0e21a273f2045c7b6a6ea002b0e139c744cb6f95e9ec044439a52883b0d74dedd1ff3da55ed140cfdddfed7fb0cccbed373de5dce1bcf + languageName: node + linkType: hard + +"bowser@npm:^2.11.0": + version: 2.11.0 + resolution: "bowser@npm:2.11.0" + checksum: 10c0/04efeecc7927a9ec33c667fa0965dea19f4ac60b3fea60793c2e6cf06c1dcd2f7ae1dbc656f450c5f50783b1c75cf9dc173ba6f3b7db2feee01f8c4b793e1bd3 + languageName: node + linkType: hard + +"brace-expansion@npm:^1.1.7": + version: 1.1.11 + resolution: "brace-expansion@npm:1.1.11" + dependencies: + balanced-match: "npm:^1.0.0" + concat-map: "npm:0.0.1" + checksum: 10c0/695a56cd058096a7cb71fb09d9d6a7070113c7be516699ed361317aca2ec169f618e28b8af352e02ab4233fb54eb0168460a40dc320bab0034b36ab59aaad668 + languageName: node + linkType: hard + +"brace-expansion@npm:^2.0.1": + version: 2.0.1 + resolution: "brace-expansion@npm:2.0.1" + dependencies: + balanced-match: "npm:^1.0.0" + checksum: 10c0/b358f2fe060e2d7a87aa015979ecea07f3c37d4018f8d6deb5bd4c229ad3a0384fe6029bb76cd8be63c81e516ee52d1a0673edbe2023d53a5191732ae3c3e49f + languageName: node + linkType: hard + +"braces@npm:^3.0.3, braces@npm:~3.0.2": + version: 3.0.3 + resolution: "braces@npm:3.0.3" + dependencies: + fill-range: "npm:^7.1.1" + checksum: 10c0/7c6dfd30c338d2997ba77500539227b9d1f85e388a5f43220865201e407e076783d0881f2d297b9f80951b4c957fcf0b51c1d2d24227631643c3f7c284b0aa04 + languageName: node + linkType: hard + +"browserslist@npm:^4.23.0, browserslist@npm:^4.23.1": + version: 4.23.2 + resolution: "browserslist@npm:4.23.2" + dependencies: + caniuse-lite: "npm:^1.0.30001640" + electron-to-chromium: "npm:^1.4.820" + node-releases: "npm:^2.0.14" + update-browserslist-db: "npm:^1.1.0" + bin: + browserslist: cli.js + checksum: 10c0/0217d23c69ed61cdd2530c7019bf7c822cd74c51f8baab18dd62457fed3129f52499f8d3a6f809ae1fb7bb3050aa70caa9a529cc36c7478427966dbf429723a5 + languageName: node + linkType: hard + +"buffer-equal-constant-time@npm:1.0.1": + version: 1.0.1 + resolution: "buffer-equal-constant-time@npm:1.0.1" + checksum: 10c0/fb2294e64d23c573d0dd1f1e7a466c3e978fe94a4e0f8183937912ca374619773bef8e2aceb854129d2efecbbc515bbd0cc78d2734a3e3031edb0888531bbc8e + languageName: node + linkType: hard + +"buffer-from@npm:^1.0.0": + version: 1.1.2 + resolution: "buffer-from@npm:1.1.2" + checksum: 10c0/124fff9d66d691a86d3b062eff4663fe437a9d9ee4b47b1b9e97f5a5d14f6d5399345db80f796827be7c95e70a8e765dd404b7c3ff3b3324f98e9b0c8826cc34 + languageName: node + linkType: hard + +"buffer@npm:^5.5.0": + version: 5.7.1 + resolution: "buffer@npm:5.7.1" + dependencies: + base64-js: "npm:^1.3.1" + ieee754: "npm:^1.1.13" + checksum: 10c0/27cac81cff434ed2876058d72e7c4789d11ff1120ef32c9de48f59eab58179b66710c488987d295ae89a228f835fc66d088652dffeb8e3ba8659f80eb091d55e + languageName: node + linkType: hard + +"busboy@npm:1.6.0, busboy@npm:^1.0.0": + version: 1.6.0 + resolution: "busboy@npm:1.6.0" + dependencies: + streamsearch: "npm:^1.1.0" + checksum: 10c0/fa7e836a2b82699b6e074393428b91ae579d4f9e21f5ac468e1b459a244341d722d2d22d10920cdd849743dbece6dca11d72de939fb75a7448825cf2babfba1f + languageName: node + linkType: hard + +"byte-size@npm:8.1.1": + version: 8.1.1 + resolution: "byte-size@npm:8.1.1" + checksum: 10c0/83170a16820fde48ebaef93bf6b2e86c5f72041f76e44eba1f3c738cceb699aeadf11088198944d5d7c6f970b465ab1e3dddc2e60bfb49a74374f3447a8db5b9 + languageName: node + linkType: hard + +"bytes@npm:3.0.0": + version: 3.0.0 + resolution: "bytes@npm:3.0.0" + checksum: 10c0/91d42c38601c76460519ffef88371caacaea483a354c8e4b8808e7b027574436a5713337c003ea3de63ee4991c2a9a637884fdfe7f761760d746929d9e8fec60 + languageName: node + linkType: hard + +"bytes@npm:3.1.2": + version: 3.1.2 + resolution: "bytes@npm:3.1.2" + checksum: 10c0/76d1c43cbd602794ad8ad2ae94095cddeb1de78c5dddaa7005c51af10b0176c69971a6d88e805a90c2b6550d76636e43c40d8427a808b8645ede885de4a0358e + languageName: node + linkType: hard + +"cacache@npm:^18.0.0, cacache@npm:^18.0.3": + version: 18.0.4 + resolution: "cacache@npm:18.0.4" + dependencies: + "@npmcli/fs": "npm:^3.1.0" + fs-minipass: "npm:^3.0.0" + glob: "npm:^10.2.2" + lru-cache: "npm:^10.0.1" + minipass: "npm:^7.0.3" + minipass-collect: "npm:^2.0.1" + minipass-flush: "npm:^1.0.5" + minipass-pipeline: "npm:^1.2.4" + p-map: "npm:^4.0.0" + ssri: "npm:^10.0.0" + tar: "npm:^6.1.11" + unique-filename: "npm:^3.0.0" + checksum: 10c0/6c055bafed9de4f3dcc64ac3dc7dd24e863210902b7c470eb9ce55a806309b3efff78033e3d8b4f7dcc5d467f2db43c6a2857aaaf26f0094b8a351d44c42179f + languageName: node + linkType: hard + +"call-bind@npm:^1.0.7": + version: 1.0.7 + resolution: "call-bind@npm:1.0.7" + dependencies: + es-define-property: "npm:^1.0.0" + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + get-intrinsic: "npm:^1.2.4" + set-function-length: "npm:^1.2.1" + checksum: 10c0/a3ded2e423b8e2a265983dba81c27e125b48eefb2655e7dfab6be597088da3d47c47976c24bc51b8fd9af1061f8f87b4ab78a314f3c77784b2ae2ba535ad8b8d + languageName: node + linkType: hard + +"callsites@npm:^3.0.0": + version: 3.1.0 + resolution: "callsites@npm:3.1.0" + checksum: 10c0/fff92277400eb06c3079f9e74f3af120db9f8ea03bad0e84d9aede54bbe2d44a56cccb5f6cf12211f93f52306df87077ecec5b712794c5a9b5dac6d615a3f301 + languageName: node + linkType: hard + +"camel-case@npm:^3.0.0": + version: 3.0.0 + resolution: "camel-case@npm:3.0.0" + dependencies: + no-case: "npm:^2.2.0" + upper-case: "npm:^1.1.1" + checksum: 10c0/491c6bbf986b9d8355e12cca6beb719b44c2fe96e8526c09958a1b4e0dbb081a82ea59c13b5a6ccf9158ce5979cbe56a8a10d7322bfeed2d84725c6b89d8f934 + languageName: node + linkType: hard + +"camelcase-css@npm:^2.0.1": + version: 2.0.1 + resolution: "camelcase-css@npm:2.0.1" + checksum: 10c0/1a1a3137e8a781e6cbeaeab75634c60ffd8e27850de410c162cce222ea331cd1ba5364e8fb21c95e5ca76f52ac34b81a090925ca00a87221355746d049c6e273 + languageName: node + linkType: hard + +"camelcase-keys@npm:^6.2.2": + version: 6.2.2 + resolution: "camelcase-keys@npm:6.2.2" + dependencies: + camelcase: "npm:^5.3.1" + map-obj: "npm:^4.0.0" + quick-lru: "npm:^4.0.1" + checksum: 10c0/bf1a28348c0f285c6c6f68fb98a9d088d3c0269fed0cdff3ea680d5a42df8a067b4de374e7a33e619eb9d5266a448fe66c2dd1f8e0c9209ebc348632882a3526 + languageName: node + linkType: hard + +"camelcase@npm:^5.3.1": + version: 5.3.1 + resolution: "camelcase@npm:5.3.1" + checksum: 10c0/92ff9b443bfe8abb15f2b1513ca182d16126359ad4f955ebc83dc4ddcc4ef3fdd2c078bc223f2673dc223488e75c99b16cc4d056624374b799e6a1555cf61b23 + languageName: node + linkType: hard + +"camelcase@npm:^6.2.0": + version: 6.3.0 + resolution: "camelcase@npm:6.3.0" + checksum: 10c0/0d701658219bd3116d12da3eab31acddb3f9440790c0792e0d398f0a520a6a4058018e546862b6fba89d7ae990efaeb97da71e1913e9ebf5a8b5621a3d55c710 + languageName: node + linkType: hard + +"caniuse-lite@npm:^1.0.30001579, caniuse-lite@npm:^1.0.30001599, caniuse-lite@npm:^1.0.30001640": + version: 1.0.30001642 + resolution: "caniuse-lite@npm:1.0.30001642" + checksum: 10c0/7366878ecdd482392a741c66fd2b39816b70573d66f64b1f8e5916835faf7a15f116368290170f4d7c4e823ec78eea9b6c0f63bee763a511cc7990afa429d63b + languageName: node + linkType: hard + +"chalk@npm:4.1.0": + version: 4.1.0 + resolution: "chalk@npm:4.1.0" + dependencies: + ansi-styles: "npm:^4.1.0" + supports-color: "npm:^7.1.0" + checksum: 10c0/3787bd65ecd98ab3a1acc3b4f71d006268a675875e49ee6ea75fb54ba73d268b97544368358c18c42445e408e076ae8ad5cec8fbad36942a2c7ac654883dc61e + languageName: node + linkType: hard + +"chalk@npm:^2.3.2, chalk@npm:^2.4.2": + version: 2.4.2 + resolution: "chalk@npm:2.4.2" + dependencies: + ansi-styles: "npm:^3.2.1" + escape-string-regexp: "npm:^1.0.5" + supports-color: "npm:^5.3.0" + checksum: 10c0/e6543f02ec877732e3a2d1c3c3323ddb4d39fbab687c23f526e25bd4c6a9bf3b83a696e8c769d078e04e5754921648f7821b2a2acfd16c550435fd630026e073 + languageName: node + linkType: hard + +"chalk@npm:^4.0.0, chalk@npm:^4.0.2, chalk@npm:^4.1.0, chalk@npm:^4.1.1": + version: 4.1.2 + resolution: "chalk@npm:4.1.2" + dependencies: + ansi-styles: "npm:^4.1.0" + supports-color: "npm:^7.1.0" + checksum: 10c0/4a3fef5cc34975c898ffe77141450f679721df9dde00f6c304353fa9c8b571929123b26a0e4617bde5018977eb655b31970c297b91b63ee83bb82aeb04666880 + languageName: node + linkType: hard + +"character-entities-legacy@npm:^1.0.0": + version: 1.1.4 + resolution: "character-entities-legacy@npm:1.1.4" + checksum: 10c0/ea4ca9c29887335eed86d78fc67a640168342b1274da84c097abb0575a253d1265281a5052f9a863979e952bcc267b4ecaaf4fe233a7e1e0d8a47806c65b96c7 + languageName: node + linkType: hard + +"character-entities@npm:^1.0.0": + version: 1.2.4 + resolution: "character-entities@npm:1.2.4" + checksum: 10c0/ad015c3d7163563b8a0ee1f587fb0ef305ef344e9fd937f79ca51cccc233786a01d591d989d5bf7b2e66b528ac9efba47f3b1897358324e69932f6d4b25adfe1 + languageName: node + linkType: hard + +"character-reference-invalid@npm:^1.0.0": + version: 1.1.4 + resolution: "character-reference-invalid@npm:1.1.4" + checksum: 10c0/29f05081c5817bd1e975b0bf61e77b60a40f62ad371d0f0ce0fdb48ab922278bc744d1fbe33771dced751887a8403f265ff634542675c8d7375f6ff4811efd0e + languageName: node + linkType: hard + +"chardet@npm:^0.7.0": + version: 0.7.0 + resolution: "chardet@npm:0.7.0" + checksum: 10c0/96e4731b9ec8050cbb56ab684e8c48d6c33f7826b755802d14e3ebfdc51c57afeece3ea39bc6b09acc359e4363525388b915e16640c1378053820f5e70d0f27d + languageName: node + linkType: hard + +"cheerio-select@npm:^2.1.0": + version: 2.1.0 + resolution: "cheerio-select@npm:2.1.0" + dependencies: + boolbase: "npm:^1.0.0" + css-select: "npm:^5.1.0" + css-what: "npm:^6.1.0" + domelementtype: "npm:^2.3.0" + domhandler: "npm:^5.0.3" + domutils: "npm:^3.0.1" + checksum: 10c0/2242097e593919dba4aacb97d7b8275def8b9ec70b00aa1f43335456870cfc9e284eae2080bdc832ed232dabb9eefcf56c722d152da4a154813fb8814a55d282 + languageName: node + linkType: hard + +"cheerio@npm:1.0.0-rc.12, cheerio@npm:^1.0.0-rc.12": + version: 1.0.0-rc.12 + resolution: "cheerio@npm:1.0.0-rc.12" + dependencies: + cheerio-select: "npm:^2.1.0" + dom-serializer: "npm:^2.0.0" + domhandler: "npm:^5.0.3" + domutils: "npm:^3.0.1" + htmlparser2: "npm:^8.0.1" + parse5: "npm:^7.0.0" + parse5-htmlparser2-tree-adapter: "npm:^7.0.0" + checksum: 10c0/c85d2f2461e3f024345b78e0bb16ad8e41492356210470dd1e7d5a91391da9fcf6c0a7cb48a9ba8820330153f0cedb4d0a60c7af15d96ecdb3092299b9d9c0cc + languageName: node + linkType: hard + +"chokidar@npm:^3.0.0, chokidar@npm:^3.5.1, chokidar@npm:^3.5.3": + version: 3.6.0 + resolution: "chokidar@npm:3.6.0" + dependencies: + anymatch: "npm:~3.1.2" + braces: "npm:~3.0.2" + fsevents: "npm:~2.3.2" + glob-parent: "npm:~5.1.2" + is-binary-path: "npm:~2.1.0" + is-glob: "npm:~4.0.1" + normalize-path: "npm:~3.0.0" + readdirp: "npm:~3.6.0" + dependenciesMeta: + fsevents: + optional: true + checksum: 10c0/8361dcd013f2ddbe260eacb1f3cb2f2c6f2b0ad118708a343a5ed8158941a39cb8fb1d272e0f389712e74ee90ce8ba864eece9e0e62b9705cb468a2f6d917462 + languageName: node + linkType: hard + +"chownr@npm:^2.0.0": + version: 2.0.0 + resolution: "chownr@npm:2.0.0" + checksum: 10c0/594754e1303672171cc04e50f6c398ae16128eb134a88f801bf5354fd96f205320f23536a045d9abd8b51024a149696e51231565891d4efdab8846021ecf88e6 + languageName: node + linkType: hard + +"ci-info@npm:^3.2.0": + version: 3.9.0 + resolution: "ci-info@npm:3.9.0" + checksum: 10c0/6f0109e36e111684291d46123d491bc4e7b7a1934c3a20dea28cba89f1d4a03acd892f5f6a81ed3855c38647e285a150e3c9ba062e38943bef57fee6c1554c3a + languageName: node + linkType: hard + +"ci-info@npm:^4.0.0": + version: 4.0.0 + resolution: "ci-info@npm:4.0.0" + checksum: 10c0/ecc003e5b60580bd081d83dd61d398ddb8607537f916313e40af4667f9c92a1243bd8e8a591a5aa78e418afec245dbe8e90a0e26e39ca0825129a99b978dd3f9 + languageName: node + linkType: hard + +"classnames@npm:^2.5.1": + version: 2.5.1 + resolution: "classnames@npm:2.5.1" + checksum: 10c0/afff4f77e62cea2d79c39962980bf316bacb0d7c49e13a21adaadb9221e1c6b9d3cdb829d8bb1b23c406f4e740507f37e1dcf506f7e3b7113d17c5bab787aa69 + languageName: node + linkType: hard + +"clean-css@npm:^4.2.1": + version: 4.2.4 + resolution: "clean-css@npm:4.2.4" + dependencies: + source-map: "npm:~0.6.0" + checksum: 10c0/0e41795fdc9d65e5e17a3b0016d90bf2a653e3a680829b5bcebdbab48604cfe36d96d8af6346338d2c2aca8aa9af024ac4fb752ac3eb5b71bef68a34a129b58a + languageName: node + linkType: hard + +"clean-stack@npm:^2.0.0": + version: 2.2.0 + resolution: "clean-stack@npm:2.2.0" + checksum: 10c0/1f90262d5f6230a17e27d0c190b09d47ebe7efdd76a03b5a1127863f7b3c9aec4c3e6c8bb3a7bbf81d553d56a1fd35728f5a8ef4c63f867ac8d690109742a8c1 + languageName: node + linkType: hard + +"cli-cursor@npm:3.1.0, cli-cursor@npm:^3.1.0": + version: 3.1.0 + resolution: "cli-cursor@npm:3.1.0" + dependencies: + restore-cursor: "npm:^3.1.0" + checksum: 10c0/92a2f98ff9037d09be3dfe1f0d749664797fb674bf388375a2207a1203b69d41847abf16434203e0089212479e47a358b13a0222ab9fccfe8e2644a7ccebd111 + languageName: node + linkType: hard + +"cli-spinners@npm:2.6.1": + version: 2.6.1 + resolution: "cli-spinners@npm:2.6.1" + checksum: 10c0/6abcdfef59aa68e6b51376d87d257f9120a0a7120a39dd21633702d24797decb6dc747dff2217c88732710db892b5053c5c672d221b6c4d13bbcb5372e203596 + languageName: node + linkType: hard + +"cli-spinners@npm:^2.5.0": + version: 2.9.2 + resolution: "cli-spinners@npm:2.9.2" + checksum: 10c0/907a1c227ddf0d7a101e7ab8b300affc742ead4b4ebe920a5bf1bc6d45dce2958fcd195eb28fa25275062fe6fa9b109b93b63bc8033396ed3bcb50297008b3a3 + languageName: node + linkType: hard + +"cli-width@npm:^3.0.0": + version: 3.0.0 + resolution: "cli-width@npm:3.0.0" + checksum: 10c0/125a62810e59a2564268c80fdff56c23159a7690c003e34aeb2e68497dccff26911998ff49c33916fcfdf71e824322cc3953e3f7b48b27267c7a062c81348a9a + languageName: node + linkType: hard + +"client-only@npm:0.0.1, client-only@npm:^0.0.1": + version: 0.0.1 + resolution: "client-only@npm:0.0.1" + checksum: 10c0/9d6cfd0c19e1c96a434605added99dff48482152af791ec4172fb912a71cff9027ff174efd8cdb2160cc7f377543e0537ffc462d4f279bc4701de3f2a3c4b358 + languageName: node + linkType: hard + +"cliui@npm:^7.0.2": + version: 7.0.4 + resolution: "cliui@npm:7.0.4" + dependencies: + string-width: "npm:^4.2.0" + strip-ansi: "npm:^6.0.0" + wrap-ansi: "npm:^7.0.0" + checksum: 10c0/6035f5daf7383470cef82b3d3db00bec70afb3423538c50394386ffbbab135e26c3689c41791f911fa71b62d13d3863c712fdd70f0fbdffd938a1e6fd09aac00 + languageName: node + linkType: hard + +"cliui@npm:^8.0.1": + version: 8.0.1 + resolution: "cliui@npm:8.0.1" + dependencies: + string-width: "npm:^4.2.0" + strip-ansi: "npm:^6.0.1" + wrap-ansi: "npm:^7.0.0" + checksum: 10c0/4bda0f09c340cbb6dfdc1ed508b3ca080f12992c18d68c6be4d9cf51756033d5266e61ec57529e610dacbf4da1c634423b0c1b11037709cc6b09045cbd815df5 + languageName: node + linkType: hard + +"clone-deep@npm:4.0.1": + version: 4.0.1 + resolution: "clone-deep@npm:4.0.1" + dependencies: + is-plain-object: "npm:^2.0.4" + kind-of: "npm:^6.0.2" + shallow-clone: "npm:^3.0.0" + checksum: 10c0/637753615aa24adf0f2d505947a1bb75e63964309034a1cf56ba4b1f30af155201edd38d26ffe26911adaae267a3c138b344a4947d39f5fc1b6d6108125aa758 + languageName: node + linkType: hard + +"clone@npm:^1.0.2": + version: 1.0.4 + resolution: "clone@npm:1.0.4" + checksum: 10c0/2176952b3649293473999a95d7bebfc9dc96410f6cbd3d2595cf12fd401f63a4bf41a7adbfd3ab2ff09ed60cb9870c58c6acdd18b87767366fabfc163700f13b + languageName: node + linkType: hard + +"clsx@npm:^2.0.0": + version: 2.1.1 + resolution: "clsx@npm:2.1.1" + checksum: 10c0/c4c8eb865f8c82baab07e71bfa8897c73454881c4f99d6bc81585aecd7c441746c1399d08363dc096c550cceaf97bd4ce1e8854e1771e9998d9f94c4fe075839 + languageName: node + linkType: hard + +"cluster-key-slot@npm:^1.1.0": + version: 1.1.2 + resolution: "cluster-key-slot@npm:1.1.2" + checksum: 10c0/d7d39ca28a8786e9e801eeb8c770e3c3236a566625d7299a47bb71113fb2298ce1039596acb82590e598c52dbc9b1f088c8f587803e697cb58e1867a95ff94d3 + languageName: node + linkType: hard + +"cmd-shim@npm:6.0.3, cmd-shim@npm:^6.0.0": + version: 6.0.3 + resolution: "cmd-shim@npm:6.0.3" + checksum: 10c0/dc09fe0bf39e86250529456d9a87dd6d5208d053e449101a600e96dc956c100e0bc312cdb413a91266201f3bd8057d4abf63875cafb99039553a1937d8f3da36 + languageName: node + linkType: hard + +"color-convert@npm:^1.9.0": + version: 1.9.3 + resolution: "color-convert@npm:1.9.3" + dependencies: + color-name: "npm:1.1.3" + checksum: 10c0/5ad3c534949a8c68fca8fbc6f09068f435f0ad290ab8b2f76841b9e6af7e0bb57b98cb05b0e19fe33f5d91e5a8611ad457e5f69e0a484caad1f7487fd0e8253c + languageName: node + linkType: hard + +"color-convert@npm:^2.0.1": + version: 2.0.1 + resolution: "color-convert@npm:2.0.1" + dependencies: + color-name: "npm:~1.1.4" + checksum: 10c0/37e1150172f2e311fe1b2df62c6293a342ee7380da7b9cfdba67ea539909afbd74da27033208d01d6d5cfc65ee7868a22e18d7e7648e004425441c0f8a15a7d7 + languageName: node + linkType: hard + +"color-name@npm:1.1.3": + version: 1.1.3 + resolution: "color-name@npm:1.1.3" + checksum: 10c0/566a3d42cca25b9b3cd5528cd7754b8e89c0eb646b7f214e8e2eaddb69994ac5f0557d9c175eb5d8f0ad73531140d9c47525085ee752a91a2ab15ab459caf6d6 + languageName: node + linkType: hard + +"color-name@npm:^1.0.0, color-name@npm:~1.1.4": + version: 1.1.4 + resolution: "color-name@npm:1.1.4" + checksum: 10c0/a1a3f914156960902f46f7f56bc62effc6c94e84b2cae157a526b1c1f74b677a47ec602bf68a61abfa2b42d15b7c5651c6dbe72a43af720bc588dff885b10f95 + languageName: node + linkType: hard + +"color-string@npm:^1.9.0": + version: 1.9.1 + resolution: "color-string@npm:1.9.1" + dependencies: + color-name: "npm:^1.0.0" + simple-swizzle: "npm:^0.2.2" + checksum: 10c0/b0bfd74c03b1f837f543898b512f5ea353f71630ccdd0d66f83028d1f0924a7d4272deb278b9aef376cacf1289b522ac3fb175e99895283645a2dc3a33af2404 + languageName: node + linkType: hard + +"color-support@npm:1.1.3, color-support@npm:^1.1.2": + version: 1.1.3 + resolution: "color-support@npm:1.1.3" + bin: + color-support: bin.js + checksum: 10c0/8ffeaa270a784dc382f62d9be0a98581db43e11eee301af14734a6d089bd456478b1a8b3e7db7ca7dc5b18a75f828f775c44074020b51c05fc00e6d0992b1cc6 + languageName: node + linkType: hard + +"color@npm:^4.2.3": + version: 4.2.3 + resolution: "color@npm:4.2.3" + dependencies: + color-convert: "npm:^2.0.1" + color-string: "npm:^1.9.0" + checksum: 10c0/7fbe7cfb811054c808349de19fb380252e5e34e61d7d168ec3353e9e9aacb1802674bddc657682e4e9730c2786592a4de6f8283e7e0d3870b829bb0b7b2f6118 + languageName: node + linkType: hard + +"columnify@npm:1.6.0": + version: 1.6.0 + resolution: "columnify@npm:1.6.0" + dependencies: + strip-ansi: "npm:^6.0.1" + wcwidth: "npm:^1.0.0" + checksum: 10c0/25b90b59129331bbb8b0c838f8df69924349b83e8eab9549f431062a20a39094b8d744bb83265be38fd5d03140ce4bfbd85837c293f618925e83157ae9535f1d + languageName: node + linkType: hard + +"combined-stream@npm:^1.0.8": + version: 1.0.8 + resolution: "combined-stream@npm:1.0.8" + dependencies: + delayed-stream: "npm:~1.0.0" + checksum: 10c0/0dbb829577e1b1e839fa82b40c07ffaf7de8a09b935cadd355a73652ae70a88b4320db322f6634a4ad93424292fa80973ac6480986247f1734a1137debf271d5 + languageName: node + linkType: hard + +"comma-separated-tokens@npm:^1.0.0": + version: 1.0.8 + resolution: "comma-separated-tokens@npm:1.0.8" + checksum: 10c0/c3bcfeaa6d50313528a006a40bcc0f9576086665c9b48d4b3a76ddd63e7d6174734386c98be1881cbf6ecfc25e1db61cd775a7b896d2ea7a65de28f83a0f9b17 + languageName: node + linkType: hard + +"commander@npm:^10.0.0": + version: 10.0.1 + resolution: "commander@npm:10.0.1" + checksum: 10c0/53f33d8927758a911094adadda4b2cbac111a5b377d8706700587650fd8f45b0bbe336de4b5c3fe47fd61f420a3d9bd452b6e0e6e5600a7e74d7bf0174f6efe3 + languageName: node + linkType: hard + +"commander@npm:^2.19.0": + version: 2.20.3 + resolution: "commander@npm:2.20.3" + checksum: 10c0/74c781a5248c2402a0a3e966a0a2bba3c054aad144f5c023364be83265e796b20565aa9feff624132ff629aa64e16999fa40a743c10c12f7c61e96a794b99288 + languageName: node + linkType: hard + +"commander@npm:^4.0.0": + version: 4.1.1 + resolution: "commander@npm:4.1.1" + checksum: 10c0/84a76c08fe6cc08c9c93f62ac573d2907d8e79138999312c92d4155bc2325d487d64d13f669b2000c9f8caf70493c1be2dac74fec3c51d5a04f8bc3ae1830bab + languageName: node + linkType: hard + +"commander@npm:^6.1.0": + version: 6.2.1 + resolution: "commander@npm:6.2.1" + checksum: 10c0/85748abd9d18c8bc88febed58b98f66b7c591d9b5017cad459565761d7b29ca13b7783ea2ee5ce84bf235897333706c4ce29adf1ce15c8252780e7000e2ce9ea + languageName: node + linkType: hard + +"commander@npm:^7.2.0": + version: 7.2.0 + resolution: "commander@npm:7.2.0" + checksum: 10c0/8d690ff13b0356df7e0ebbe6c59b4712f754f4b724d4f473d3cc5b3fdcf978e3a5dc3078717858a2ceb50b0f84d0660a7f22a96cdc50fb877d0c9bb31593d23a + languageName: node + linkType: hard + +"common-ancestor-path@npm:^1.0.1": + version: 1.0.1 + resolution: "common-ancestor-path@npm:1.0.1" + checksum: 10c0/390c08d2a67a7a106d39499c002d827d2874966d938012453fd7ca34cd306881e2b9d604f657fa7a8e6e4896d67f39ebc09bf1bfd8da8ff318e0fb7a8752c534 + languageName: node + linkType: hard + +"compare-func@npm:^2.0.0": + version: 2.0.0 + resolution: "compare-func@npm:2.0.0" + dependencies: + array-ify: "npm:^1.0.0" + dot-prop: "npm:^5.1.0" + checksum: 10c0/78bd4dd4ed311a79bd264c9e13c36ed564cde657f1390e699e0f04b8eee1fc06ffb8698ce2dfb5fbe7342d509579c82d4e248f08915b708f77f7b72234086cc3 + languageName: node + linkType: hard + +"compressible@npm:~2.0.16": + version: 2.0.18 + resolution: "compressible@npm:2.0.18" + dependencies: + mime-db: "npm:>= 1.43.0 < 2" + checksum: 10c0/8a03712bc9f5b9fe530cc5a79e164e665550d5171a64575d7dcf3e0395d7b4afa2d79ab176c61b5b596e28228b350dd07c1a2a6ead12fd81d1b6cd632af2fef7 + languageName: node + linkType: hard + +"compression@npm:^1.7.4": + version: 1.7.4 + resolution: "compression@npm:1.7.4" + dependencies: + accepts: "npm:~1.3.5" + bytes: "npm:3.0.0" + compressible: "npm:~2.0.16" + debug: "npm:2.6.9" + on-headers: "npm:~1.0.2" + safe-buffer: "npm:5.1.2" + vary: "npm:~1.1.2" + checksum: 10c0/138db836202a406d8a14156a5564fb1700632a76b6e7d1546939472895a5304f2b23c80d7a22bf44c767e87a26e070dbc342ea63bb45ee9c863354fa5556bbbc + languageName: node + linkType: hard + +"concat-map@npm:0.0.1": + version: 0.0.1 + resolution: "concat-map@npm:0.0.1" + checksum: 10c0/c996b1cfdf95b6c90fee4dae37e332c8b6eb7d106430c17d538034c0ad9a1630cb194d2ab37293b1bdd4d779494beee7786d586a50bd9376fd6f7bcc2bd4c98f + languageName: node + linkType: hard + +"concat-stream@npm:^1.5.2": + version: 1.6.2 + resolution: "concat-stream@npm:1.6.2" + dependencies: + buffer-from: "npm:^1.0.0" + inherits: "npm:^2.0.3" + readable-stream: "npm:^2.2.2" + typedarray: "npm:^0.0.6" + checksum: 10c0/2e9864e18282946dabbccb212c5c7cec0702745e3671679eb8291812ca7fd12023f7d8cb36493942a62f770ac96a7f90009dc5c82ad69893438371720fa92617 + languageName: node + linkType: hard + +"concat-stream@npm:^2.0.0": + version: 2.0.0 + resolution: "concat-stream@npm:2.0.0" + dependencies: + buffer-from: "npm:^1.0.0" + inherits: "npm:^2.0.3" + readable-stream: "npm:^3.0.2" + typedarray: "npm:^0.0.6" + checksum: 10c0/29565dd9198fe1d8cf57f6cc71527dbc6ad67e12e4ac9401feb389c53042b2dceedf47034cbe702dfc4fd8df3ae7e6bfeeebe732cc4fa2674e484c13f04c219a + languageName: node + linkType: hard + +"config-chain@npm:^1.1.13": + version: 1.1.13 + resolution: "config-chain@npm:1.1.13" + dependencies: + ini: "npm:^1.3.4" + proto-list: "npm:~1.2.1" + checksum: 10c0/39d1df18739d7088736cc75695e98d7087aea43646351b028dfabd5508d79cf6ef4c5bcd90471f52cd87ae470d1c5490c0a8c1a292fbe6ee9ff688061ea0963e + languageName: node + linkType: hard + +"console-control-strings@npm:^1.0.0, console-control-strings@npm:^1.1.0": + version: 1.1.0 + resolution: "console-control-strings@npm:1.1.0" + checksum: 10c0/7ab51d30b52d461412cd467721bb82afe695da78fff8f29fe6f6b9cbaac9a2328e27a22a966014df9532100f6dd85370460be8130b9c677891ba36d96a343f50 + languageName: node + linkType: hard + +"content-disposition@npm:0.5.4": + version: 0.5.4 + resolution: "content-disposition@npm:0.5.4" + dependencies: + safe-buffer: "npm:5.2.1" + checksum: 10c0/bac0316ebfeacb8f381b38285dc691c9939bf0a78b0b7c2d5758acadad242d04783cee5337ba7d12a565a19075af1b3c11c728e1e4946de73c6ff7ce45f3f1bb + languageName: node + linkType: hard + +"content-type@npm:~1.0.4, content-type@npm:~1.0.5": + version: 1.0.5 + resolution: "content-type@npm:1.0.5" + checksum: 10c0/b76ebed15c000aee4678c3707e0860cb6abd4e680a598c0a26e17f0bfae723ec9cc2802f0ff1bc6e4d80603719010431d2231018373d4dde10f9ccff9dadf5af + languageName: node + linkType: hard + +"conventional-changelog-angular@npm:7.0.0": + version: 7.0.0 + resolution: "conventional-changelog-angular@npm:7.0.0" + dependencies: + compare-func: "npm:^2.0.0" + checksum: 10c0/90e73e25e224059b02951b6703b5f8742dc2a82c1fea62163978e6735fd3ab04350897a8fc6f443ec6b672d6b66e28a0820e833e544a0101f38879e5e6289b7e + languageName: node + linkType: hard + +"conventional-changelog-core@npm:5.0.1": + version: 5.0.1 + resolution: "conventional-changelog-core@npm:5.0.1" + dependencies: + add-stream: "npm:^1.0.0" + conventional-changelog-writer: "npm:^6.0.0" + conventional-commits-parser: "npm:^4.0.0" + dateformat: "npm:^3.0.3" + get-pkg-repo: "npm:^4.2.1" + git-raw-commits: "npm:^3.0.0" + git-remote-origin-url: "npm:^2.0.0" + git-semver-tags: "npm:^5.0.0" + normalize-package-data: "npm:^3.0.3" + read-pkg: "npm:^3.0.0" + read-pkg-up: "npm:^3.0.0" + checksum: 10c0/c026da415ea58346c167e58f8dd717592e92afc897aa604189a6d69f48b6943e7a656b2c83433810feea32dda117b0914a7f5860ed338a21f6ee9b0f56788b37 + languageName: node + linkType: hard + +"conventional-changelog-preset-loader@npm:^3.0.0": + version: 3.0.0 + resolution: "conventional-changelog-preset-loader@npm:3.0.0" + checksum: 10c0/5de23c4aa8b8526c3542fd5abe9758d56eed79821f32cc16d1fdf480cecc44855edbe4680113f229509dcaf4b97cc41e786ac8e3b0822b44fd9d0b98542ed0e0 + languageName: node + linkType: hard + +"conventional-changelog-writer@npm:^6.0.0": + version: 6.0.1 + resolution: "conventional-changelog-writer@npm:6.0.1" + dependencies: + conventional-commits-filter: "npm:^3.0.0" + dateformat: "npm:^3.0.3" + handlebars: "npm:^4.7.7" + json-stringify-safe: "npm:^5.0.1" + meow: "npm:^8.1.2" + semver: "npm:^7.0.0" + split: "npm:^1.0.1" + bin: + conventional-changelog-writer: cli.js + checksum: 10c0/50790b0d92e06c5ab1c02cc4eb2ecd74575244d31cfacea1885d7c8afeae1bc7bbc169140fe062f2438b9952400762240b796e59521c0246278859296b323338 + languageName: node + linkType: hard + +"conventional-commits-filter@npm:^3.0.0": + version: 3.0.0 + resolution: "conventional-commits-filter@npm:3.0.0" + dependencies: + lodash.ismatch: "npm:^4.4.0" + modify-values: "npm:^1.0.1" + checksum: 10c0/9d43cf9029bf39b70b394c551846a57b6f0473028ba5628c38bd447672655cc27bb80ba502d9a7e41335f63ad62b754cb26579f3d4bae7398dfc092acbb32578 + languageName: node + linkType: hard + +"conventional-commits-parser@npm:^4.0.0": + version: 4.0.0 + resolution: "conventional-commits-parser@npm:4.0.0" + dependencies: + JSONStream: "npm:^1.3.5" + is-text-path: "npm:^1.0.1" + meow: "npm:^8.1.2" + split2: "npm:^3.2.2" + bin: + conventional-commits-parser: cli.js + checksum: 10c0/12e390cc80ad8a825c5775a329b95e11cf47a6df7b8a3875d375e28b8cb27c4f32955842ea73e4e357cff9757a6be99fdffe4fda87a23e9d8e73f983425537a0 + languageName: node + linkType: hard + +"conventional-recommended-bump@npm:7.0.1": + version: 7.0.1 + resolution: "conventional-recommended-bump@npm:7.0.1" + dependencies: + concat-stream: "npm:^2.0.0" + conventional-changelog-preset-loader: "npm:^3.0.0" + conventional-commits-filter: "npm:^3.0.0" + conventional-commits-parser: "npm:^4.0.0" + git-raw-commits: "npm:^3.0.0" + git-semver-tags: "npm:^5.0.0" + meow: "npm:^8.1.2" + bin: + conventional-recommended-bump: cli.js + checksum: 10c0/ff751a256ddfbec62efd5a32de059b01659e945073793c6766143a8242864fd8099804a90bbf1e6a61928ade3d12292d6f66f721a113630de392d54eb7f0b0c3 + languageName: node + linkType: hard + +"convert-source-map@npm:^2.0.0": + version: 2.0.0 + resolution: "convert-source-map@npm:2.0.0" + checksum: 10c0/8f2f7a27a1a011cc6cc88cc4da2d7d0cfa5ee0369508baae3d98c260bb3ac520691464e5bbe4ae7cdf09860c1d69ecc6f70c63c6e7c7f7e3f18ec08484dc7d9b + languageName: node + linkType: hard + +"cookie-parser@npm:^1.4.6": + version: 1.4.6 + resolution: "cookie-parser@npm:1.4.6" + dependencies: + cookie: "npm:0.4.1" + cookie-signature: "npm:1.0.6" + checksum: 10c0/9c2ade5459290802cd472a2d2a6e46fbd7de3e8514e02bfed5edfde892d77733c7f89d9d2015f752a9087680429b416972d7aba748bf6824e21eb680c8556383 + languageName: node + linkType: hard + +"cookie-signature@npm:1.0.6": + version: 1.0.6 + resolution: "cookie-signature@npm:1.0.6" + checksum: 10c0/b36fd0d4e3fef8456915fcf7742e58fbfcc12a17a018e0eb9501c9d5ef6893b596466f03b0564b81af29ff2538fd0aa4b9d54fe5ccbfb4c90ea50ad29fe2d221 + languageName: node + linkType: hard + +"cookie@npm:0.4.1": + version: 0.4.1 + resolution: "cookie@npm:0.4.1" + checksum: 10c0/4d7bc798df3d0f34035977949cd6b7d05bbab47d7dcb868667f460b578a550cd20dec923832b8a3a107ef35aba091a3975e14f79efacf6e39282dc0fed6db4a1 + languageName: node + linkType: hard + +"cookie@npm:0.6.0": + version: 0.6.0 + resolution: "cookie@npm:0.6.0" + checksum: 10c0/f2318b31af7a31b4ddb4a678d024514df5e705f9be5909a192d7f116cfb6d45cbacf96a473fa733faa95050e7cff26e7832bb3ef94751592f1387b71c8956686 + languageName: node + linkType: hard + +"core-js-compat@npm:^3.36.1, core-js-compat@npm:^3.37.1": + version: 3.37.1 + resolution: "core-js-compat@npm:3.37.1" + dependencies: + browserslist: "npm:^4.23.0" + checksum: 10c0/4e2da9c900f2951a57947af7aeef4d16f2c75d7f7e966c0d0b62953f65225003ade5e84d3ae98847f65b24c109c606821d9dc925db8ca418fb761e7c81963c2a + languageName: node + linkType: hard + +"core-util-is@npm:~1.0.0": + version: 1.0.3 + resolution: "core-util-is@npm:1.0.3" + checksum: 10c0/90a0e40abbddfd7618f8ccd63a74d88deea94e77d0e8dbbea059fa7ebebb8fbb4e2909667fe26f3a467073de1a542ebe6ae4c73a73745ac5833786759cd906c9 + languageName: node + linkType: hard + +"cors@npm:^2.8.5": + version: 2.8.5 + resolution: "cors@npm:2.8.5" + dependencies: + object-assign: "npm:^4" + vary: "npm:^1" + checksum: 10c0/373702b7999409922da80de4a61938aabba6929aea5b6fd9096fefb9e8342f626c0ebd7507b0e8b0b311380744cc985f27edebc0a26e0ddb784b54e1085de761 + languageName: node + linkType: hard + +"cosmiconfig@npm:^8.1.3, cosmiconfig@npm:^8.2.0": + version: 8.3.6 + resolution: "cosmiconfig@npm:8.3.6" + dependencies: + import-fresh: "npm:^3.3.0" + js-yaml: "npm:^4.1.0" + parse-json: "npm:^5.2.0" + path-type: "npm:^4.0.0" + peerDependencies: + typescript: ">=4.9.5" + peerDependenciesMeta: + typescript: + optional: true + checksum: 10c0/0382a9ed13208f8bfc22ca2f62b364855207dffdb73dc26e150ade78c3093f1cf56172df2dd460c8caf2afa91c0ed4ec8a88c62f8f9cd1cf423d26506aa8797a + languageName: node + linkType: hard + +"create-require@npm:^1.1.0": + version: 1.1.1 + resolution: "create-require@npm:1.1.1" + checksum: 10c0/157cbc59b2430ae9a90034a5f3a1b398b6738bf510f713edc4d4e45e169bc514d3d99dd34d8d01ca7ae7830b5b8b537e46ae8f3c8f932371b0875c0151d7ec91 + languageName: node + linkType: hard + +"crelt@npm:^1.0.0": + version: 1.0.6 + resolution: "crelt@npm:1.0.6" + checksum: 10c0/e0fb76dff50c5eb47f2ea9b786c17f9425c66276025adee80876bdbf4a84ab72e899e56d3928431ab0cb057a105ef704df80fe5726ef0f7b1658f815521bdf09 + languageName: node + linkType: hard + +"cross-env@npm:^7.0.3": + version: 7.0.3 + resolution: "cross-env@npm:7.0.3" + dependencies: + cross-spawn: "npm:^7.0.1" + bin: + cross-env: src/bin/cross-env.js + cross-env-shell: src/bin/cross-env-shell.js + checksum: 10c0/f3765c25746c69fcca369655c442c6c886e54ccf3ab8c16847d5ad0e91e2f337d36eedc6599c1227904bf2a228d721e690324446876115bc8e7b32a866735ecf + languageName: node + linkType: hard + +"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.1, cross-spawn@npm:^7.0.3": + version: 7.0.3 + resolution: "cross-spawn@npm:7.0.3" + dependencies: + path-key: "npm:^3.1.0" + shebang-command: "npm:^2.0.0" + which: "npm:^2.0.1" + checksum: 10c0/5738c312387081c98d69c98e105b6327b069197f864a60593245d64c8089c8a0a744e16349281210d56835bb9274130d825a78b2ad6853ca13cfbeffc0c31750 + languageName: node + linkType: hard + +"css-select@npm:^5.1.0": + version: 5.1.0 + resolution: "css-select@npm:5.1.0" + dependencies: + boolbase: "npm:^1.0.0" + css-what: "npm:^6.1.0" + domhandler: "npm:^5.0.2" + domutils: "npm:^3.0.1" + nth-check: "npm:^2.0.1" + checksum: 10c0/551c60dba5b54054741032c1793b5734f6ba45e23ae9e82761a3c0ed1acbb8cfedfa443aaba3a3c1a54cac12b456d2012a09d2cd5f0e82e430454c1b9d84d500 + languageName: node + linkType: hard + +"css-tree@npm:^2.3.1": + version: 2.3.1 + resolution: "css-tree@npm:2.3.1" + dependencies: + mdn-data: "npm:2.0.30" + source-map-js: "npm:^1.0.1" + checksum: 10c0/6f8c1a11d5e9b14bf02d10717fc0351b66ba12594166f65abfbd8eb8b5b490dd367f5c7721db241a3c792d935fc6751fbc09f7e1598d421477ad9fadc30f4f24 + languageName: node + linkType: hard + +"css-tree@npm:~2.2.0": + version: 2.2.1 + resolution: "css-tree@npm:2.2.1" + dependencies: + mdn-data: "npm:2.0.28" + source-map-js: "npm:^1.0.1" + checksum: 10c0/47e87b0f02f8ac22f57eceb65c58011dd142d2158128882a0bf963cf2eabb81a4ebbc2e3790c8289be7919fa8b83750c7b69272bd66772c708143b772ba3c186 + languageName: node + linkType: hard + +"css-what@npm:^6.1.0": + version: 6.1.0 + resolution: "css-what@npm:6.1.0" + checksum: 10c0/a09f5a6b14ba8dcf57ae9a59474722e80f20406c53a61e9aedb0eedc693b135113ffe2983f4efc4b5065ae639442e9ae88df24941ef159c218b231011d733746 + languageName: node + linkType: hard + +"cssesc@npm:^3.0.0": + version: 3.0.0 + resolution: "cssesc@npm:3.0.0" + bin: + cssesc: bin/cssesc + checksum: 10c0/6bcfd898662671be15ae7827120472c5667afb3d7429f1f917737f3bf84c4176003228131b643ae74543f17a394446247df090c597bb9a728cce298606ed0aa7 + languageName: node + linkType: hard + +"csso@npm:^5.0.5": + version: 5.0.5 + resolution: "csso@npm:5.0.5" + dependencies: + css-tree: "npm:~2.2.0" + checksum: 10c0/ab4beb1e97dd7e207c10e9925405b45f15a6cd1b4880a8686ad573aa6d476aed28b4121a666cffd26c37a26179f7b54741f7c257543003bfb244d06a62ad569b + languageName: node + linkType: hard + +"csstype@npm:^3.0.2": + version: 3.1.3 + resolution: "csstype@npm:3.1.3" + checksum: 10c0/80c089d6f7e0c5b2bd83cf0539ab41474198579584fa10d86d0cafe0642202343cbc119e076a0b1aece191989477081415d66c9fefbf3c957fc2fc4b7009f248 + languageName: node + linkType: hard + +"d3-array@npm:2 - 3, d3-array@npm:2.10.0 - 3, d3-array@npm:^3.1.6": + version: 3.2.4 + resolution: "d3-array@npm:3.2.4" + dependencies: + internmap: "npm:1 - 2" + checksum: 10c0/08b95e91130f98c1375db0e0af718f4371ccacef7d5d257727fe74f79a24383e79aba280b9ffae655483ffbbad4fd1dec4ade0119d88c4749f388641c8bf8c50 + languageName: node + linkType: hard + +"d3-color@npm:1 - 3": + version: 3.1.0 + resolution: "d3-color@npm:3.1.0" + checksum: 10c0/a4e20e1115fa696fce041fbe13fbc80dc4c19150fa72027a7c128ade980bc0eeeba4bcf28c9e21f0bce0e0dbfe7ca5869ef67746541dcfda053e4802ad19783c + languageName: node + linkType: hard + +"d3-ease@npm:^3.0.1": + version: 3.0.1 + resolution: "d3-ease@npm:3.0.1" + checksum: 10c0/fec8ef826c0cc35cda3092c6841e07672868b1839fcaf556e19266a3a37e6bc7977d8298c0fcb9885e7799bfdcef7db1baaba9cd4dcf4bc5e952cf78574a88b0 + languageName: node + linkType: hard + +"d3-format@npm:1 - 3": + version: 3.1.0 + resolution: "d3-format@npm:3.1.0" + checksum: 10c0/049f5c0871ebce9859fc5e2f07f336b3c5bfff52a2540e0bac7e703fce567cd9346f4ad1079dd18d6f1e0eaa0599941c1810898926f10ac21a31fd0a34b4aa75 + languageName: node + linkType: hard + +"d3-interpolate@npm:1.2.0 - 3, d3-interpolate@npm:^3.0.1": + version: 3.0.1 + resolution: "d3-interpolate@npm:3.0.1" + dependencies: + d3-color: "npm:1 - 3" + checksum: 10c0/19f4b4daa8d733906671afff7767c19488f51a43d251f8b7f484d5d3cfc36c663f0a66c38fe91eee30f40327443d799be17169f55a293a3ba949e84e57a33e6a + languageName: node + linkType: hard + +"d3-path@npm:^3.1.0": + version: 3.1.0 + resolution: "d3-path@npm:3.1.0" + checksum: 10c0/dc1d58ec87fa8319bd240cf7689995111a124b141428354e9637aa83059eb12e681f77187e0ada5dedfce346f7e3d1f903467ceb41b379bfd01cd8e31721f5da + languageName: node + linkType: hard + +"d3-scale@npm:^4.0.2": + version: 4.0.2 + resolution: "d3-scale@npm:4.0.2" + dependencies: + d3-array: "npm:2.10.0 - 3" + d3-format: "npm:1 - 3" + d3-interpolate: "npm:1.2.0 - 3" + d3-time: "npm:2.1.1 - 3" + d3-time-format: "npm:2 - 4" + checksum: 10c0/65d9ad8c2641aec30ed5673a7410feb187a224d6ca8d1a520d68a7d6eac9d04caedbff4713d1e8545be33eb7fec5739983a7ab1d22d4e5ad35368c6729d362f1 + languageName: node + linkType: hard + +"d3-shape@npm:^3.1.0": + version: 3.2.0 + resolution: "d3-shape@npm:3.2.0" + dependencies: + d3-path: "npm:^3.1.0" + checksum: 10c0/f1c9d1f09926daaf6f6193ae3b4c4b5521e81da7d8902d24b38694517c7f527ce3c9a77a9d3a5722ad1e3ff355860b014557b450023d66a944eabf8cfde37132 + languageName: node + linkType: hard + +"d3-time-format@npm:2 - 4": + version: 4.1.0 + resolution: "d3-time-format@npm:4.1.0" + dependencies: + d3-time: "npm:1 - 3" + checksum: 10c0/735e00fb25a7fd5d418fac350018713ae394eefddb0d745fab12bbff0517f9cdb5f807c7bbe87bb6eeb06249662f8ea84fec075f7d0cd68609735b2ceb29d206 + languageName: node + linkType: hard + +"d3-time@npm:1 - 3, d3-time@npm:2.1.1 - 3, d3-time@npm:^3.0.0": + version: 3.1.0 + resolution: "d3-time@npm:3.1.0" + dependencies: + d3-array: "npm:2 - 3" + checksum: 10c0/a984f77e1aaeaa182679b46fbf57eceb6ebdb5f67d7578d6f68ef933f8eeb63737c0949991618a8d29472dbf43736c7d7f17c452b2770f8c1271191cba724ca1 + languageName: node + linkType: hard + +"d3-timer@npm:^3.0.1": + version: 3.0.1 + resolution: "d3-timer@npm:3.0.1" + checksum: 10c0/d4c63cb4bb5461d7038aac561b097cd1c5673969b27cbdd0e87fa48d9300a538b9e6f39b4a7f0e3592ef4f963d858c8a9f0e92754db73116770856f2fc04561a + languageName: node + linkType: hard + +"dargs@npm:^7.0.0": + version: 7.0.0 + resolution: "dargs@npm:7.0.0" + checksum: 10c0/ec7f6a8315a8fa2f8b12d39207615bdf62b4d01f631b96fbe536c8ad5469ab9ed710d55811e564d0d5c1d548fc8cb6cc70bf0939f2415790159f5a75e0f96c92 + languageName: node + linkType: hard + +"dateformat@npm:^3.0.3": + version: 3.0.3 + resolution: "dateformat@npm:3.0.3" + checksum: 10c0/2effb8bef52ff912f87a05e4adbeacff46353e91313ad1ea9ed31412db26849f5a0fcc7e3ce36dbfb84fc6c881a986d5694f84838ad0da7000d5150693e78678 + languageName: node + linkType: hard + +"dayjs@npm:^1.11.12": + version: 1.11.12 + resolution: "dayjs@npm:1.11.12" + checksum: 10c0/9673d37f3f9ad8a91caaeae9b3fea9a0010c81c7f58599fb9d860bc3359b86632fbff8eb7dddc86c2acaab01c5e6860bc672952f17b58c9286140c52b077c8e4 + languageName: node + linkType: hard + +"debug@npm:2.6.9": + version: 2.6.9 + resolution: "debug@npm:2.6.9" + dependencies: + ms: "npm:2.0.0" + checksum: 10c0/121908fb839f7801180b69a7e218a40b5a0b718813b886b7d6bdb82001b931c938e2941d1e4450f33a1b1df1da653f5f7a0440c197f29fbf8a6e9d45ff6ef589 + languageName: node + linkType: hard + +"debug@npm:4, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.4": + version: 4.3.5 + resolution: "debug@npm:4.3.5" + dependencies: + ms: "npm:2.1.2" + peerDependenciesMeta: + supports-color: + optional: true + checksum: 10c0/082c375a2bdc4f4469c99f325ff458adad62a3fc2c482d59923c260cb08152f34e2659f72b3767db8bb2f21ca81a60a42d1019605a412132d7b9f59363a005cc + languageName: node + linkType: hard + +"decamelize-keys@npm:^1.1.0": + version: 1.1.1 + resolution: "decamelize-keys@npm:1.1.1" + dependencies: + decamelize: "npm:^1.1.0" + map-obj: "npm:^1.0.0" + checksum: 10c0/4ca385933127437658338c65fb9aead5f21b28d3dd3ccd7956eb29aab0953b5d3c047fbc207111672220c71ecf7a4d34f36c92851b7bbde6fca1a02c541bdd7d + languageName: node + linkType: hard + +"decamelize@npm:^1.1.0": + version: 1.2.0 + resolution: "decamelize@npm:1.2.0" + checksum: 10c0/85c39fe8fbf0482d4a1e224ef0119db5c1897f8503bcef8b826adff7a1b11414972f6fef2d7dec2ee0b4be3863cf64ac1439137ae9e6af23a3d8dcbe26a5b4b2 + languageName: node + linkType: hard + +"decimal.js-light@npm:^2.4.1": + version: 2.5.1 + resolution: "decimal.js-light@npm:2.5.1" + checksum: 10c0/4fd33f535aac9e5bd832796831b65d9ec7914ad129c7437b3ab991b0c2eaaa5a57e654e6174c4a17f1b3895ea366f0c1ab4955cdcdf7cfdcf3ad5a58b456c020 + languageName: node + linkType: hard + +"dedent@npm:1.5.3": + version: 1.5.3 + resolution: "dedent@npm:1.5.3" + peerDependencies: + babel-plugin-macros: ^3.1.0 + peerDependenciesMeta: + babel-plugin-macros: + optional: true + checksum: 10c0/d94bde6e6f780be4da4fd760288fcf755ec368872f4ac5218197200d86430aeb8d90a003a840bff1c20221188e3f23adced0119cb811c6873c70d0ac66d12832 + languageName: node + linkType: hard + +"deepmerge@npm:^4.3.1": + version: 4.3.1 + resolution: "deepmerge@npm:4.3.1" + checksum: 10c0/e53481aaf1aa2c4082b5342be6b6d8ad9dfe387bc92ce197a66dea08bd4265904a087e75e464f14d1347cf2ac8afe1e4c16b266e0561cc5df29382d3c5f80044 + languageName: node + linkType: hard + +"defaults@npm:^1.0.3": + version: 1.0.4 + resolution: "defaults@npm:1.0.4" + dependencies: + clone: "npm:^1.0.2" + checksum: 10c0/9cfbe498f5c8ed733775db62dfd585780387d93c17477949e1670bfcfb9346e0281ce8c4bf9f4ac1fc0f9b851113bd6dc9e41182ea1644ccd97de639fa13c35a + languageName: node + linkType: hard + +"define-data-property@npm:^1.1.4": + version: 1.1.4 + resolution: "define-data-property@npm:1.1.4" + dependencies: + es-define-property: "npm:^1.0.0" + es-errors: "npm:^1.3.0" + gopd: "npm:^1.0.1" + checksum: 10c0/dea0606d1483eb9db8d930d4eac62ca0fa16738b0b3e07046cddfacf7d8c868bbe13fa0cb263eb91c7d0d527960dc3f2f2471a69ed7816210307f6744fe62e37 + languageName: node + linkType: hard + +"define-lazy-prop@npm:^2.0.0": + version: 2.0.0 + resolution: "define-lazy-prop@npm:2.0.0" + checksum: 10c0/db6c63864a9d3b7dc9def55d52764968a5af296de87c1b2cc71d8be8142e445208071953649e0386a8cc37cfcf9a2067a47207f1eb9ff250c2a269658fdae422 + languageName: node + linkType: hard + +"delayed-stream@npm:~1.0.0": + version: 1.0.0 + resolution: "delayed-stream@npm:1.0.0" + checksum: 10c0/d758899da03392e6712f042bec80aa293bbe9e9ff1b2634baae6a360113e708b91326594c8a486d475c69d6259afb7efacdc3537bfcda1c6c648e390ce601b19 + languageName: node + linkType: hard + +"delegates@npm:^1.0.0": + version: 1.0.0 + resolution: "delegates@npm:1.0.0" + checksum: 10c0/ba05874b91148e1db4bf254750c042bf2215febd23a6d3cda2e64896aef79745fbd4b9996488bd3cafb39ce19dbce0fd6e3b6665275638befffe1c9b312b91b5 + languageName: node + linkType: hard + +"denque@npm:^2.1.0": + version: 2.1.0 + resolution: "denque@npm:2.1.0" + checksum: 10c0/f9ef81aa0af9c6c614a727cb3bd13c5d7db2af1abf9e6352045b86e85873e629690f6222f4edd49d10e4ccf8f078bbeec0794fafaf61b659c0589d0c511ec363 + languageName: node + linkType: hard + +"depd@npm:2.0.0, depd@npm:~2.0.0": + version: 2.0.0 + resolution: "depd@npm:2.0.0" + checksum: 10c0/58bd06ec20e19529b06f7ad07ddab60e504d9e0faca4bd23079fac2d279c3594334d736508dc350e06e510aba5e22e4594483b3a6562ce7c17dd797f4cc4ad2c + languageName: node + linkType: hard + +"deprecation@npm:^2.0.0": + version: 2.3.1 + resolution: "deprecation@npm:2.3.1" + checksum: 10c0/23d688ba66b74d09b908c40a76179418acbeeb0bfdf218c8075c58ad8d0c315130cb91aa3dffb623aa3a411a3569ce56c6460de6c8d69071c17fe6dd2442f032 + languageName: node + linkType: hard + +"destroy@npm:1.2.0": + version: 1.2.0 + resolution: "destroy@npm:1.2.0" + checksum: 10c0/bd7633942f57418f5a3b80d5cb53898127bcf53e24cdf5d5f4396be471417671f0fee48a4ebe9a1e9defbde2a31280011af58a57e090ff822f589b443ed4e643 + languageName: node + linkType: hard + +"detect-indent@npm:^5.0.0": + version: 5.0.0 + resolution: "detect-indent@npm:5.0.0" + checksum: 10c0/58d985dd5b4d5e5aad6fe7d8ecc74538fa92c807c894794b8505569e45651bf01a38755b65d9d3d17e512239a26d3131837cbef43cf4226968d5abf175bbcc9d + languageName: node + linkType: hard + +"detect-libc@npm:^2.0.0, detect-libc@npm:^2.0.3": + version: 2.0.3 + resolution: "detect-libc@npm:2.0.3" + checksum: 10c0/88095bda8f90220c95f162bf92cad70bd0e424913e655c20578600e35b91edc261af27531cf160a331e185c0ced93944bc7e09939143225f56312d7fd800fdb7 + languageName: node + linkType: hard + +"detect-node@npm:2.1.0, detect-node@npm:^2.0.4": + version: 2.1.0 + resolution: "detect-node@npm:2.1.0" + checksum: 10c0/f039f601790f2e9d4654e499913259a798b1f5246ae24f86ab5e8bd4aaf3bce50484234c494f11fb00aecb0c6e2733aa7b1cf3f530865640b65fbbd65b2c4e09 + languageName: node + linkType: hard + +"didyoumean@npm:^1.2.2": + version: 1.2.2 + resolution: "didyoumean@npm:1.2.2" + checksum: 10c0/95d0b53d23b851aacff56dfadb7ecfedce49da4232233baecfeecb7710248c4aa03f0aa8995062f0acafaf925adf8536bd7044a2e68316fd7d411477599bc27b + languageName: node + linkType: hard + +"diff-sequences@npm:^29.6.3": + version: 29.6.3 + resolution: "diff-sequences@npm:29.6.3" + checksum: 10c0/32e27ac7dbffdf2fb0eb5a84efd98a9ad084fbabd5ac9abb8757c6770d5320d2acd172830b28c4add29bb873d59420601dfc805ac4064330ce59b1adfd0593b2 + languageName: node + linkType: hard + +"diff@npm:^4.0.1": + version: 4.0.2 + resolution: "diff@npm:4.0.2" + checksum: 10c0/81b91f9d39c4eaca068eb0c1eb0e4afbdc5bb2941d197f513dd596b820b956fef43485876226d65d497bebc15666aa2aa82c679e84f65d5f2bfbf14ee46e32c1 + languageName: node + linkType: hard + +"dir-glob@npm:^3.0.1": + version: 3.0.1 + resolution: "dir-glob@npm:3.0.1" + dependencies: + path-type: "npm:^4.0.0" + checksum: 10c0/dcac00920a4d503e38bb64001acb19df4efc14536ada475725e12f52c16777afdee4db827f55f13a908ee7efc0cb282e2e3dbaeeb98c0993dd93d1802d3bf00c + languageName: node + linkType: hard + +"dlv@npm:^1.1.3": + version: 1.1.3 + resolution: "dlv@npm:1.1.3" + checksum: 10c0/03eb4e769f19a027fd5b43b59e8a05e3fd2100ac239ebb0bf9a745de35d449e2f25cfaf3aa3934664551d72856f4ae8b7822016ce5c42c2d27c18ae79429ec42 + languageName: node + linkType: hard + +"dom-helpers@npm:^5.0.1": + version: 5.2.1 + resolution: "dom-helpers@npm:5.2.1" + dependencies: + "@babel/runtime": "npm:^7.8.7" + csstype: "npm:^3.0.2" + checksum: 10c0/f735074d66dd759b36b158fa26e9d00c9388ee0e8c9b16af941c38f014a37fc80782de83afefd621681b19ac0501034b4f1c4a3bff5caa1b8667f0212b5e124c + languageName: node + linkType: hard + +"dom-serializer@npm:^1.0.1": + version: 1.4.1 + resolution: "dom-serializer@npm:1.4.1" + dependencies: + domelementtype: "npm:^2.0.1" + domhandler: "npm:^4.2.0" + entities: "npm:^2.0.0" + checksum: 10c0/67d775fa1ea3de52035c98168ddcd59418356943b5eccb80e3c8b3da53adb8e37edb2cc2f885802b7b1765bf5022aec21dfc32910d7f9e6de4c3148f095ab5e0 + languageName: node + linkType: hard + +"dom-serializer@npm:^2.0.0": + version: 2.0.0 + resolution: "dom-serializer@npm:2.0.0" + dependencies: + domelementtype: "npm:^2.3.0" + domhandler: "npm:^5.0.2" + entities: "npm:^4.2.0" + checksum: 10c0/d5ae2b7110ca3746b3643d3ef60ef823f5f078667baf530cec096433f1627ec4b6fa8c072f09d079d7cda915fd2c7bc1b7b935681e9b09e591e1e15f4040b8e2 + languageName: node + linkType: hard + +"domelementtype@npm:^2.0.1, domelementtype@npm:^2.2.0, domelementtype@npm:^2.3.0": + version: 2.3.0 + resolution: "domelementtype@npm:2.3.0" + checksum: 10c0/686f5a9ef0fff078c1412c05db73a0dce096190036f33e400a07e2a4518e9f56b1e324f5c576a0a747ef0e75b5d985c040b0d51945ce780c0dd3c625a18cd8c9 + languageName: node + linkType: hard + +"domhandler@npm:^3.3.0": + version: 3.3.0 + resolution: "domhandler@npm:3.3.0" + dependencies: + domelementtype: "npm:^2.0.1" + checksum: 10c0/376e6462a6144121f6ae50c9c1b8e0b22d2e0c68f9fb2ef6e57a5f4f9395854b1258cb638c58b171ee291359a5f41a4a57f403954db976484a59ffcee4c1e405 + languageName: node + linkType: hard + +"domhandler@npm:^4.2.0": + version: 4.3.1 + resolution: "domhandler@npm:4.3.1" + dependencies: + domelementtype: "npm:^2.2.0" + checksum: 10c0/5c199c7468cb052a8b5ab80b13528f0db3d794c64fc050ba793b574e158e67c93f8336e87fd81e9d5ee43b0e04aea4d8b93ed7be4899cb726a1601b3ba18538b + languageName: node + linkType: hard + +"domhandler@npm:^5.0.2, domhandler@npm:^5.0.3": + version: 5.0.3 + resolution: "domhandler@npm:5.0.3" + dependencies: + domelementtype: "npm:^2.3.0" + checksum: 10c0/bba1e5932b3e196ad6862286d76adc89a0dbf0c773e5ced1eb01f9af930c50093a084eff14b8de5ea60b895c56a04d5de8bbc4930c5543d029091916770b2d2a + languageName: node + linkType: hard + +"domutils@npm:^2.4.2": + version: 2.8.0 + resolution: "domutils@npm:2.8.0" + dependencies: + dom-serializer: "npm:^1.0.1" + domelementtype: "npm:^2.2.0" + domhandler: "npm:^4.2.0" + checksum: 10c0/d58e2ae01922f0dd55894e61d18119924d88091837887bf1438f2327f32c65eb76426bd9384f81e7d6dcfb048e0f83c19b222ad7101176ad68cdc9c695b563db + languageName: node + linkType: hard + +"domutils@npm:^3.0.1, domutils@npm:^3.1.0": + version: 3.1.0 + resolution: "domutils@npm:3.1.0" + dependencies: + dom-serializer: "npm:^2.0.0" + domelementtype: "npm:^2.3.0" + domhandler: "npm:^5.0.3" + checksum: 10c0/342d64cf4d07b8a0573fb51e0a6312a88fb520c7fefd751870bf72fa5fc0f2e0cb9a3958a573610b1d608c6e2a69b8e9b4b40f0bfb8f87a71bce4f180cca1887 + languageName: node + linkType: hard + +"dot-case@npm:^3.0.4": + version: 3.0.4 + resolution: "dot-case@npm:3.0.4" + dependencies: + no-case: "npm:^3.0.4" + tslib: "npm:^2.0.3" + checksum: 10c0/5b859ea65097a7ea870e2c91b5768b72ddf7fa947223fd29e167bcdff58fe731d941c48e47a38ec8aa8e43044c8fbd15cd8fa21689a526bc34b6548197cd5b05 + languageName: node + linkType: hard + +"dot-prop@npm:^5.1.0": + version: 5.3.0 + resolution: "dot-prop@npm:5.3.0" + dependencies: + is-obj: "npm:^2.0.0" + checksum: 10c0/93f0d343ef87fe8869320e62f2459f7e70f49c6098d948cc47e060f4a3f827d0ad61e83cb82f2bd90cd5b9571b8d334289978a43c0f98fea4f0e99ee8faa0599 + languageName: node + linkType: hard + +"dotenv-expand@npm:~11.0.6": + version: 11.0.6 + resolution: "dotenv-expand@npm:11.0.6" + dependencies: + dotenv: "npm:^16.4.4" + checksum: 10c0/e22891ec72cb926d46d9a26290ef77f9cc9ddcba92d2f83d5e6f3a803d1590887be68e25b559415d080053000441b6f63f5b36093a565bb8c5c994b992ae49f2 + languageName: node + linkType: hard + +"dotenv@npm:^16.4.4, dotenv@npm:^16.4.5, dotenv@npm:~16.4.5": + version: 16.4.5 + resolution: "dotenv@npm:16.4.5" + checksum: 10c0/48d92870076832af0418b13acd6e5a5a3e83bb00df690d9812e94b24aff62b88ade955ac99a05501305b8dc8f1b0ee7638b18493deb6fe93d680e5220936292f + languageName: node + linkType: hard + +"duplexer@npm:^0.1.1": + version: 0.1.2 + resolution: "duplexer@npm:0.1.2" + checksum: 10c0/c57bcd4bdf7e623abab2df43a7b5b23d18152154529d166c1e0da6bee341d84c432d157d7e97b32fecb1bf3a8b8857dd85ed81a915789f550637ed25b8e64fc2 + languageName: node + linkType: hard + +"dynamic-dedupe@npm:^0.3.0": + version: 0.3.0 + resolution: "dynamic-dedupe@npm:0.3.0" + dependencies: + xtend: "npm:^4.0.0" + checksum: 10c0/505a79f05221daaa5b6d4b6dddc30881809a136810acea138bf56e784b15c237077864ae18824b5dfb0f836a321d14cec0b7cec004e6abf31c38a1e9862af22b + languageName: node + linkType: hard + +"eastasianwidth@npm:^0.2.0": + version: 0.2.0 + resolution: "eastasianwidth@npm:0.2.0" + checksum: 10c0/26f364ebcdb6395f95124fda411f63137a4bfb5d3a06453f7f23dfe52502905bd84e0488172e0f9ec295fdc45f05c23d5d91baf16bd26f0fe9acd777a188dc39 + languageName: node + linkType: hard + +"ecdsa-sig-formatter@npm:1.0.11": + version: 1.0.11 + resolution: "ecdsa-sig-formatter@npm:1.0.11" + dependencies: + safe-buffer: "npm:^5.0.1" + checksum: 10c0/ebfbf19d4b8be938f4dd4a83b8788385da353d63307ede301a9252f9f7f88672e76f2191618fd8edfc2f24679236064176fab0b78131b161ee73daa37125408c + languageName: node + linkType: hard + +"editorconfig@npm:^1.0.4": + version: 1.0.4 + resolution: "editorconfig@npm:1.0.4" + dependencies: + "@one-ini/wasm": "npm:0.1.1" + commander: "npm:^10.0.0" + minimatch: "npm:9.0.1" + semver: "npm:^7.5.3" + bin: + editorconfig: bin/editorconfig + checksum: 10c0/ed6985959d7b34a56e1c09bef118758c81c969489b768d152c93689fce8403b0452462e934f665febaba3478eebc0fd41c0a36100783eaadf6d926c4abc87a3d + languageName: node + linkType: hard + +"ee-first@npm:1.1.1": + version: 1.1.1 + resolution: "ee-first@npm:1.1.1" + checksum: 10c0/b5bb125ee93161bc16bfe6e56c6b04de5ad2aa44234d8f644813cc95d861a6910903132b05093706de2b706599367c4130eb6d170f6b46895686b95f87d017b7 + languageName: node + linkType: hard + +"ejs@npm:^3.1.7": + version: 3.1.10 + resolution: "ejs@npm:3.1.10" + dependencies: + jake: "npm:^10.8.5" + bin: + ejs: bin/cli.js + checksum: 10c0/52eade9e68416ed04f7f92c492183340582a36482836b11eab97b159fcdcfdedc62233a1bf0bf5e5e1851c501f2dca0e2e9afd111db2599e4e7f53ee29429ae1 + languageName: node + linkType: hard + +"electron-to-chromium@npm:^1.4.820": + version: 1.4.832 + resolution: "electron-to-chromium@npm:1.4.832" + checksum: 10c0/40d427b513bdfdadaced02163d702a74fe8662cb8d696570fd641f5cef42f362180ea2b4259ab2f3aab30bca66748358f2b552d00ae7915f731ce27bf8052816 + languageName: node + linkType: hard + +"emoji-regex@npm:^8.0.0": + version: 8.0.0 + resolution: "emoji-regex@npm:8.0.0" + checksum: 10c0/b6053ad39951c4cf338f9092d7bfba448cdfd46fe6a2a034700b149ac9ffbc137e361cbd3c442297f86bed2e5f7576c1b54cc0a6bf8ef5106cc62f496af35010 + languageName: node + linkType: hard + +"emoji-regex@npm:^9.2.2": + version: 9.2.2 + resolution: "emoji-regex@npm:9.2.2" + checksum: 10c0/af014e759a72064cf66e6e694a7fc6b0ed3d8db680427b021a89727689671cefe9d04151b2cad51dbaf85d5ba790d061cd167f1cf32eb7b281f6368b3c181639 + languageName: node + linkType: hard + +"encodeurl@npm:~1.0.2": + version: 1.0.2 + resolution: "encodeurl@npm:1.0.2" + checksum: 10c0/f6c2387379a9e7c1156c1c3d4f9cb7bb11cf16dd4c1682e1f6746512564b053df5781029b6061296832b59fb22f459dbe250386d217c2f6e203601abb2ee0bec + languageName: node + linkType: hard + +"encoding@npm:^0.1.13": + version: 0.1.13 + resolution: "encoding@npm:0.1.13" + dependencies: + iconv-lite: "npm:^0.6.2" + checksum: 10c0/36d938712ff00fe1f4bac88b43bcffb5930c1efa57bbcdca9d67e1d9d6c57cfb1200fb01efe0f3109b2ce99b231f90779532814a81370a1bd3274a0f58585039 + languageName: node + linkType: hard + +"end-of-stream@npm:^1.4.1": + version: 1.4.4 + resolution: "end-of-stream@npm:1.4.4" + dependencies: + once: "npm:^1.4.0" + checksum: 10c0/870b423afb2d54bb8d243c63e07c170409d41e20b47eeef0727547aea5740bd6717aca45597a9f2745525667a6b804c1e7bede41f856818faee5806dd9ff3975 + languageName: node + linkType: hard + +"enquirer@npm:~2.3.6": + version: 2.3.6 + resolution: "enquirer@npm:2.3.6" + dependencies: + ansi-colors: "npm:^4.1.1" + checksum: 10c0/8e070e052c2c64326a2803db9084d21c8aaa8c688327f133bf65c4a712586beb126fd98c8a01cfb0433e82a4bd3b6262705c55a63e0f7fb91d06b9cedbde9a11 + languageName: node + linkType: hard + +"entities@npm:^2.0.0": + version: 2.2.0 + resolution: "entities@npm:2.2.0" + checksum: 10c0/7fba6af1f116300d2ba1c5673fc218af1961b20908638391b4e1e6d5850314ee2ac3ec22d741b3a8060479911c99305164aed19b6254bde75e7e6b1b2c3f3aa3 + languageName: node + linkType: hard + +"entities@npm:^4.2.0, entities@npm:^4.4.0, entities@npm:^4.5.0": + version: 4.5.0 + resolution: "entities@npm:4.5.0" + checksum: 10c0/5b039739f7621f5d1ad996715e53d964035f75ad3b9a4d38c6b3804bb226e282ffeae2443624d8fdd9c47d8e926ae9ac009c54671243f0c3294c26af7cc85250 + languageName: node + linkType: hard + +"env-paths@npm:^2.2.0": + version: 2.2.1 + resolution: "env-paths@npm:2.2.1" + checksum: 10c0/285325677bf00e30845e330eec32894f5105529db97496ee3f598478e50f008c5352a41a30e5e72ec9de8a542b5a570b85699cd63bd2bc646dbcb9f311d83bc4 + languageName: node + linkType: hard + +"envinfo@npm:7.13.0": + version: 7.13.0 + resolution: "envinfo@npm:7.13.0" + bin: + envinfo: dist/cli.js + checksum: 10c0/9c279213cbbb353b3171e8e333fd2ed564054abade08ab3d735fe136e10a0e14e0588e1ce77e6f01285f2462eaca945d64f0778be5ae3d9e82804943e36a4411 + languageName: node + linkType: hard + +"err-code@npm:^2.0.2": + version: 2.0.3 + resolution: "err-code@npm:2.0.3" + checksum: 10c0/b642f7b4dd4a376e954947550a3065a9ece6733ab8e51ad80db727aaae0817c2e99b02a97a3d6cecc648a97848305e728289cf312d09af395403a90c9d4d8a66 + languageName: node + linkType: hard + +"error-ex@npm:^1.3.1": + version: 1.3.2 + resolution: "error-ex@npm:1.3.2" + dependencies: + is-arrayish: "npm:^0.2.1" + checksum: 10c0/ba827f89369b4c93382cfca5a264d059dfefdaa56ecc5e338ffa58a6471f5ed93b71a20add1d52290a4873d92381174382658c885ac1a2305f7baca363ce9cce + languageName: node + linkType: hard + +"es-define-property@npm:^1.0.0": + version: 1.0.0 + resolution: "es-define-property@npm:1.0.0" + dependencies: + get-intrinsic: "npm:^1.2.4" + checksum: 10c0/6bf3191feb7ea2ebda48b577f69bdfac7a2b3c9bcf97307f55fd6ef1bbca0b49f0c219a935aca506c993d8c5d8bddd937766cb760cd5e5a1071351f2df9f9aa4 + languageName: node + linkType: hard + +"es-errors@npm:^1.3.0": + version: 1.3.0 + resolution: "es-errors@npm:1.3.0" + checksum: 10c0/0a61325670072f98d8ae3b914edab3559b6caa980f08054a3b872052640d91da01d38df55df797fcc916389d77fc92b8d5906cf028f4db46d7e3003abecbca85 + languageName: node + linkType: hard + +"escalade@npm:^3.1.1, escalade@npm:^3.1.2": + version: 3.1.2 + resolution: "escalade@npm:3.1.2" + checksum: 10c0/6b4adafecd0682f3aa1cd1106b8fff30e492c7015b178bc81b2d2f75106dabea6c6d6e8508fc491bd58e597c74abb0e8e2368f943ecb9393d4162e3c2f3cf287 + languageName: node + linkType: hard + +"escape-goat@npm:^3.0.0": + version: 3.0.0 + resolution: "escape-goat@npm:3.0.0" + checksum: 10c0/a2b470bbdb95ccbcd19390576993a2b75735457b1979275f4f0d6da86d2e932a2a12edd9270208e3090299a26df857da1f80555c37bb1bac6fa9135322253ca4 + languageName: node + linkType: hard + +"escape-html@npm:~1.0.3": + version: 1.0.3 + resolution: "escape-html@npm:1.0.3" + checksum: 10c0/524c739d776b36c3d29fa08a22e03e8824e3b2fd57500e5e44ecf3cc4707c34c60f9ca0781c0e33d191f2991161504c295e98f68c78fe7baa6e57081ec6ac0a3 + languageName: node + linkType: hard + +"escape-string-regexp@npm:^1.0.5": + version: 1.0.5 + resolution: "escape-string-regexp@npm:1.0.5" + checksum: 10c0/a968ad453dd0c2724e14a4f20e177aaf32bb384ab41b674a8454afe9a41c5e6fe8903323e0a1052f56289d04bd600f81278edf140b0fcc02f5cac98d0f5b5371 + languageName: node + linkType: hard + +"escape-string-regexp@npm:^4.0.0": + version: 4.0.0 + resolution: "escape-string-regexp@npm:4.0.0" + checksum: 10c0/9497d4dd307d845bd7f75180d8188bb17ea8c151c1edbf6b6717c100e104d629dc2dfb687686181b0f4b7d732c7dfdc4d5e7a8ff72de1b0ca283a75bbb3a9cd9 + languageName: node + linkType: hard + +"esprima@npm:^4.0.0": + version: 4.0.1 + resolution: "esprima@npm:4.0.1" + bin: + esparse: ./bin/esparse.js + esvalidate: ./bin/esvalidate.js + checksum: 10c0/ad4bab9ead0808cf56501750fd9d3fb276f6b105f987707d059005d57e182d18a7c9ec7f3a01794ebddcca676773e42ca48a32d67a250c9d35e009ca613caba3 + languageName: node + linkType: hard + +"esutils@npm:^2.0.2": + version: 2.0.3 + resolution: "esutils@npm:2.0.3" + checksum: 10c0/9a2fe69a41bfdade834ba7c42de4723c97ec776e40656919c62cbd13607c45e127a003f05f724a1ea55e5029a4cf2de444b13009f2af71271e42d93a637137c7 + languageName: node + linkType: hard + +"etag@npm:~1.8.1": + version: 1.8.1 + resolution: "etag@npm:1.8.1" + checksum: 10c0/12be11ef62fb9817314d790089a0a49fae4e1b50594135dcb8076312b7d7e470884b5100d249b28c18581b7fd52f8b485689ffae22a11ed9ec17377a33a08f84 + languageName: node + linkType: hard + +"eventemitter3@npm:^4.0.1, eventemitter3@npm:^4.0.4": + version: 4.0.7 + resolution: "eventemitter3@npm:4.0.7" + checksum: 10c0/5f6d97cbcbac47be798e6355e3a7639a84ee1f7d9b199a07017f1d2f1e2fe236004d14fa5dfaeba661f94ea57805385e326236a6debbc7145c8877fbc0297c6b + languageName: node + linkType: hard + +"execa@npm:5.0.0": + version: 5.0.0 + resolution: "execa@npm:5.0.0" + dependencies: + cross-spawn: "npm:^7.0.3" + get-stream: "npm:^6.0.0" + human-signals: "npm:^2.1.0" + is-stream: "npm:^2.0.0" + merge-stream: "npm:^2.0.0" + npm-run-path: "npm:^4.0.1" + onetime: "npm:^5.1.2" + signal-exit: "npm:^3.0.3" + strip-final-newline: "npm:^2.0.0" + checksum: 10c0/e110add7ca0de63aea415385ebad7236c8de281d5d9a916dbd69f59009dac3d5d631e6252c2ea5d0258220b0d22acf25649b2caf05fa162eaa1401339fc69ba4 + languageName: node + linkType: hard + +"exponential-backoff@npm:^3.1.1": + version: 3.1.1 + resolution: "exponential-backoff@npm:3.1.1" + checksum: 10c0/160456d2d647e6019640bd07111634d8c353038d9fa40176afb7cd49b0548bdae83b56d05e907c2cce2300b81cae35d800ef92fefb9d0208e190fa3b7d6bb579 + languageName: node + linkType: hard + +"express-async-errors@npm:^3.1.1": + version: 3.1.1 + resolution: "express-async-errors@npm:3.1.1" + peerDependencies: + express: ^4.16.2 + checksum: 10c0/56c4e90c44e98c7edc5bd38e18dd23b0d9a7139cb94ff3e25943ba257415b433e0e52ea8f9bc1fb5b70a5e6c5246eaace4fb69ab171edfb8896580928bb97ec6 + languageName: node + linkType: hard + +"express@npm:^4.16.3, express@npm:^4.19.2": + version: 4.19.2 + resolution: "express@npm:4.19.2" + dependencies: + accepts: "npm:~1.3.8" + array-flatten: "npm:1.1.1" + body-parser: "npm:1.20.2" + content-disposition: "npm:0.5.4" + content-type: "npm:~1.0.4" + cookie: "npm:0.6.0" + cookie-signature: "npm:1.0.6" + debug: "npm:2.6.9" + depd: "npm:2.0.0" + encodeurl: "npm:~1.0.2" + escape-html: "npm:~1.0.3" + etag: "npm:~1.8.1" + finalhandler: "npm:1.2.0" + fresh: "npm:0.5.2" + http-errors: "npm:2.0.0" + merge-descriptors: "npm:1.0.1" + methods: "npm:~1.1.2" + on-finished: "npm:2.4.1" + parseurl: "npm:~1.3.3" + path-to-regexp: "npm:0.1.7" + proxy-addr: "npm:~2.0.7" + qs: "npm:6.11.0" + range-parser: "npm:~1.2.1" + safe-buffer: "npm:5.2.1" + send: "npm:0.18.0" + serve-static: "npm:1.15.0" + setprototypeof: "npm:1.2.0" + statuses: "npm:2.0.1" + type-is: "npm:~1.6.18" + utils-merge: "npm:1.0.1" + vary: "npm:~1.1.2" + checksum: 10c0/e82e2662ea9971c1407aea9fc3c16d6b963e55e3830cd0ef5e00b533feda8b770af4e3be630488ef8a752d7c75c4fcefb15892868eeaafe7353cb9e3e269fdcb + languageName: node + linkType: hard + +"external-editor@npm:^3.0.3": + version: 3.1.0 + resolution: "external-editor@npm:3.1.0" + dependencies: + chardet: "npm:^0.7.0" + iconv-lite: "npm:^0.4.24" + tmp: "npm:^0.0.33" + checksum: 10c0/c98f1ba3efdfa3c561db4447ff366a6adb5c1e2581462522c56a18bf90dfe4da382f9cd1feee3e330108c3595a854b218272539f311ba1b3298f841eb0fbf339 + languageName: node + linkType: hard + +"fast-equals@npm:^5.0.1": + version: 5.0.1 + resolution: "fast-equals@npm:5.0.1" + checksum: 10c0/d7077b8b681036c2840ed9860a3048e44fc268fad2b525b8f25b43458be0c8ad976152eb4b475de9617170423c5b802121ebb61ed6641c3ac035fadaf805c8c0 + languageName: node + linkType: hard + +"fast-glob@npm:^3.2.9, fast-glob@npm:^3.3.0": + version: 3.3.2 + resolution: "fast-glob@npm:3.3.2" + dependencies: + "@nodelib/fs.stat": "npm:^2.0.2" + "@nodelib/fs.walk": "npm:^1.2.3" + glob-parent: "npm:^5.1.2" + merge2: "npm:^1.3.0" + micromatch: "npm:^4.0.4" + checksum: 10c0/42baad7b9cd40b63e42039132bde27ca2cb3a4950d0a0f9abe4639ea1aa9d3e3b40f98b1fe31cbc0cc17b664c9ea7447d911a152fa34ec5b72977b125a6fc845 + languageName: node + linkType: hard + +"fast-xml-parser@npm:4.2.5": + version: 4.2.5 + resolution: "fast-xml-parser@npm:4.2.5" + dependencies: + strnum: "npm:^1.0.5" + bin: + fxparser: src/cli/cli.js + checksum: 10c0/f422349189b70660238eff9e48c57a0b9e5142f4c442bd79f50049847006341fe8dbcaac899c54e219034f63249fdba4512542ec54ef4dec24fcf9f54ad20d42 + languageName: node + linkType: hard + +"fastq@npm:^1.6.0": + version: 1.17.1 + resolution: "fastq@npm:1.17.1" + dependencies: + reusify: "npm:^1.0.4" + checksum: 10c0/1095f16cea45fb3beff558bb3afa74ca7a9250f5a670b65db7ed585f92b4b48381445cd328b3d87323da81e43232b5d5978a8201bde84e0cd514310f1ea6da34 + languageName: node + linkType: hard + +"fault@npm:^1.0.0": + version: 1.0.4 + resolution: "fault@npm:1.0.4" + dependencies: + format: "npm:^0.2.0" + checksum: 10c0/c86c11500c1b676787296f31ade8473adcc6784f118f07c1a9429730b6288d0412f96e069ce010aa57e4f65a9cccb5abee8868bbe3c5f10de63b20482c9baebd + languageName: node + linkType: hard + +"figures@npm:3.2.0, figures@npm:^3.0.0": + version: 3.2.0 + resolution: "figures@npm:3.2.0" + dependencies: + escape-string-regexp: "npm:^1.0.5" + checksum: 10c0/9c421646ede432829a50bc4e55c7a4eb4bcb7cc07b5bab2f471ef1ab9a344595bbebb6c5c21470093fbb730cd81bbca119624c40473a125293f656f49cb47629 + languageName: node + linkType: hard + +"figures@npm:^2.0.0": + version: 2.0.0 + resolution: "figures@npm:2.0.0" + dependencies: + escape-string-regexp: "npm:^1.0.5" + checksum: 10c0/5dc5a75fec3e7e04ae65d6ce51d28b3e70d4656c51b06996b6fdb2cb5b542df512e3b3c04482f5193a964edddafa5521479ff948fa84e12ff556e53e094ab4ce + languageName: node + linkType: hard + +"filelist@npm:^1.0.4": + version: 1.0.4 + resolution: "filelist@npm:1.0.4" + dependencies: + minimatch: "npm:^5.0.1" + checksum: 10c0/426b1de3944a3d153b053f1c0ebfd02dccd0308a4f9e832ad220707a6d1f1b3c9784d6cadf6b2f68f09a57565f63ebc7bcdc913ccf8012d834f472c46e596f41 + languageName: node + linkType: hard + +"fill-range@npm:^7.1.1": + version: 7.1.1 + resolution: "fill-range@npm:7.1.1" + dependencies: + to-regex-range: "npm:^5.0.1" + checksum: 10c0/b75b691bbe065472f38824f694c2f7449d7f5004aa950426a2c28f0306c60db9b880c0b0e4ed819997ffb882d1da02cfcfc819bddc94d71627f5269682edf018 + languageName: node + linkType: hard + +"finalhandler@npm:1.2.0": + version: 1.2.0 + resolution: "finalhandler@npm:1.2.0" + dependencies: + debug: "npm:2.6.9" + encodeurl: "npm:~1.0.2" + escape-html: "npm:~1.0.3" + on-finished: "npm:2.4.1" + parseurl: "npm:~1.3.3" + statuses: "npm:2.0.1" + unpipe: "npm:~1.0.0" + checksum: 10c0/64b7e5ff2ad1fcb14931cd012651631b721ce657da24aedb5650ddde9378bf8e95daa451da43398123f5de161a81e79ff5affe4f9f2a6d2df4a813d6d3e254b7 + languageName: node + linkType: hard + +"find-up@npm:^2.0.0": + version: 2.1.0 + resolution: "find-up@npm:2.1.0" + dependencies: + locate-path: "npm:^2.0.0" + checksum: 10c0/c080875c9fe28eb1962f35cbe83c683796a0321899f1eed31a37577800055539815de13d53495049697d3ba313013344f843bb9401dd337a1b832be5edfc6840 + languageName: node + linkType: hard + +"find-up@npm:^4.0.0, find-up@npm:^4.1.0": + version: 4.1.0 + resolution: "find-up@npm:4.1.0" + dependencies: + locate-path: "npm:^5.0.0" + path-exists: "npm:^4.0.0" + checksum: 10c0/0406ee89ebeefa2d507feb07ec366bebd8a6167ae74aa4e34fb4c4abd06cf782a3ce26ae4194d70706f72182841733f00551c209fe575cb00bd92104056e78c1 + languageName: node + linkType: hard + +"flat@npm:^5.0.2": + version: 5.0.2 + resolution: "flat@npm:5.0.2" + bin: + flat: cli.js + checksum: 10c0/f178b13482f0cd80c7fede05f4d10585b1f2fdebf26e12edc138e32d3150c6ea6482b7f12813a1091143bad52bb6d3596bca51a162257a21163c0ff438baa5fe + languageName: node + linkType: hard + +"follow-redirects@npm:^1.15.6": + version: 1.15.6 + resolution: "follow-redirects@npm:1.15.6" + peerDependenciesMeta: + debug: + optional: true + checksum: 10c0/9ff767f0d7be6aa6870c82ac79cf0368cd73e01bbc00e9eb1c2a16fbb198ec105e3c9b6628bb98e9f3ac66fe29a957b9645bcb9a490bb7aa0d35f908b6b85071 + languageName: node + linkType: hard + +"foreground-child@npm:^3.1.0": + version: 3.2.1 + resolution: "foreground-child@npm:3.2.1" + dependencies: + cross-spawn: "npm:^7.0.0" + signal-exit: "npm:^4.0.1" + checksum: 10c0/9a53a33dbd87090e9576bef65fb4a71de60f6863a8062a7b11bc1cbe3cc86d428677d7c0b9ef61cdac11007ac580006f78bd5638618d564cfd5e6fd713d6878f + languageName: node + linkType: hard + +"form-data@npm:^4.0.0": + version: 4.0.0 + resolution: "form-data@npm:4.0.0" + dependencies: + asynckit: "npm:^0.4.0" + combined-stream: "npm:^1.0.8" + mime-types: "npm:^2.1.12" + checksum: 10c0/cb6f3ac49180be03ff07ba3ff125f9eba2ff0b277fb33c7fc47569fc5e616882c5b1c69b9904c4c4187e97dd0419dd03b134174756f296dec62041e6527e2c6e + languageName: node + linkType: hard + +"format@npm:^0.2.0": + version: 0.2.2 + resolution: "format@npm:0.2.2" + checksum: 10c0/6032ba747541a43abf3e37b402b2f72ee08ebcb58bf84d816443dd228959837f1cddf1e8775b29fa27ff133f4bd146d041bfca5f9cf27f048edf3d493cf8fee6 + languageName: node + linkType: hard + +"forwarded@npm:0.2.0": + version: 0.2.0 + resolution: "forwarded@npm:0.2.0" + checksum: 10c0/9b67c3fac86acdbc9ae47ba1ddd5f2f81526fa4c8226863ede5600a3f7c7416ef451f6f1e240a3cc32d0fd79fcfe6beb08fd0da454f360032bde70bf80afbb33 + languageName: node + linkType: hard + +"fraction.js@npm:^4.3.7": + version: 4.3.7 + resolution: "fraction.js@npm:4.3.7" + checksum: 10c0/df291391beea9ab4c263487ffd9d17fed162dbb736982dee1379b2a8cc94e4e24e46ed508c6d278aded9080ba51872f1bc5f3a5fd8d7c74e5f105b508ac28711 + languageName: node + linkType: hard + +"framer-motion@npm:^11.3.7": + version: 11.3.8 + resolution: "framer-motion@npm:11.3.8" + dependencies: + tslib: "npm:^2.4.0" + peerDependencies: + "@emotion/is-prop-valid": "*" + react: ^18.0.0 + react-dom: ^18.0.0 + peerDependenciesMeta: + "@emotion/is-prop-valid": + optional: true + react: + optional: true + react-dom: + optional: true + checksum: 10c0/71c7ff866694f531d01d4d6f44c2eef620abab1acf52436716b3c82e5ae45be544b4cf45b1a15686e17daa214a89fae3523077624919e6f9df9a53d3c46509f0 + languageName: node + linkType: hard + +"fresh@npm:0.5.2": + version: 0.5.2 + resolution: "fresh@npm:0.5.2" + checksum: 10c0/c6d27f3ed86cc5b601404822f31c900dd165ba63fff8152a3ef714e2012e7535027063bc67ded4cb5b3a49fa596495d46cacd9f47d6328459cf570f08b7d9e5a + languageName: node + linkType: hard + +"front-matter@npm:^4.0.2": + version: 4.0.2 + resolution: "front-matter@npm:4.0.2" + dependencies: + js-yaml: "npm:^3.13.1" + checksum: 10c0/7a0df5ca37428dd563c057bc17a8940481fe53876609bcdc443a02ce463c70f1842c7cb4628b80916de46a253732794b36fb6a31105db0f185698a93acee4011 + languageName: node + linkType: hard + +"fs-constants@npm:^1.0.0": + version: 1.0.0 + resolution: "fs-constants@npm:1.0.0" + checksum: 10c0/a0cde99085f0872f4d244e83e03a46aa387b74f5a5af750896c6b05e9077fac00e9932fdf5aef84f2f16634cd473c63037d7a512576da7d5c2b9163d1909f3a8 + languageName: node + linkType: hard + +"fs-extra@npm:^11.1.0, fs-extra@npm:^11.2.0": + version: 11.2.0 + resolution: "fs-extra@npm:11.2.0" + dependencies: + graceful-fs: "npm:^4.2.0" + jsonfile: "npm:^6.0.1" + universalify: "npm:^2.0.0" + checksum: 10c0/d77a9a9efe60532d2e790e938c81a02c1b24904ef7a3efb3990b835514465ba720e99a6ea56fd5e2db53b4695319b644d76d5a0e9988a2beef80aa7b1da63398 + languageName: node + linkType: hard + +"fs-minipass@npm:^2.0.0": + version: 2.1.0 + resolution: "fs-minipass@npm:2.1.0" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10c0/703d16522b8282d7299337539c3ed6edddd1afe82435e4f5b76e34a79cd74e488a8a0e26a636afc2440e1a23b03878e2122e3a2cfe375a5cf63c37d92b86a004 + languageName: node + linkType: hard + +"fs-minipass@npm:^3.0.0": + version: 3.0.3 + resolution: "fs-minipass@npm:3.0.3" + dependencies: + minipass: "npm:^7.0.3" + checksum: 10c0/63e80da2ff9b621e2cb1596abcb9207f1cf82b968b116ccd7b959e3323144cce7fb141462200971c38bbf2ecca51695069db45265705bed09a7cd93ae5b89f94 + languageName: node + linkType: hard + +"fs.realpath@npm:^1.0.0": + version: 1.0.0 + resolution: "fs.realpath@npm:1.0.0" + checksum: 10c0/444cf1291d997165dfd4c0d58b69f0e4782bfd9149fd72faa4fe299e68e0e93d6db941660b37dd29153bf7186672ececa3b50b7e7249477b03fdf850f287c948 + languageName: node + linkType: hard + +"fsevents@npm:~2.3.2": + version: 2.3.3 + resolution: "fsevents@npm:2.3.3" + dependencies: + node-gyp: "npm:latest" + checksum: 10c0/a1f0c44595123ed717febbc478aa952e47adfc28e2092be66b8ab1635147254ca6cfe1df792a8997f22716d4cbafc73309899ff7bfac2ac3ad8cf2e4ecc3ec60 + conditions: os=darwin + languageName: node + linkType: hard + +"fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin": + version: 2.3.3 + resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin::version=2.3.3&hash=df0bf1" + dependencies: + node-gyp: "npm:latest" + conditions: os=darwin + languageName: node + linkType: hard + +"function-bind@npm:^1.1.2": + version: 1.1.2 + resolution: "function-bind@npm:1.1.2" + checksum: 10c0/d8680ee1e5fcd4c197e4ac33b2b4dce03c71f4d91717292785703db200f5c21f977c568d28061226f9b5900cbcd2c84463646134fd5337e7925e0942bc3f46d5 + languageName: node + linkType: hard + +"gauge@npm:^3.0.0": + version: 3.0.2 + resolution: "gauge@npm:3.0.2" + dependencies: + aproba: "npm:^1.0.3 || ^2.0.0" + color-support: "npm:^1.1.2" + console-control-strings: "npm:^1.0.0" + has-unicode: "npm:^2.0.1" + object-assign: "npm:^4.1.1" + signal-exit: "npm:^3.0.0" + string-width: "npm:^4.2.3" + strip-ansi: "npm:^6.0.1" + wide-align: "npm:^1.1.2" + checksum: 10c0/75230ccaf216471e31025c7d5fcea1629596ca20792de50c596eb18ffb14d8404f927cd55535aab2eeecd18d1e11bd6f23ec3c2e9878d2dda1dc74bccc34b913 + languageName: node + linkType: hard + +"gensync@npm:^1.0.0-beta.2": + version: 1.0.0-beta.2 + resolution: "gensync@npm:1.0.0-beta.2" + checksum: 10c0/782aba6cba65b1bb5af3b095d96249d20edbe8df32dbf4696fd49be2583faf676173bf4809386588828e4dd76a3354fcbeb577bab1c833ccd9fc4577f26103f8 + languageName: node + linkType: hard + +"get-caller-file@npm:^2.0.5": + version: 2.0.5 + resolution: "get-caller-file@npm:2.0.5" + checksum: 10c0/c6c7b60271931fa752aeb92f2b47e355eac1af3a2673f47c9589e8f8a41adc74d45551c1bc57b5e66a80609f10ffb72b6f575e4370d61cc3f7f3aaff01757cde + languageName: node + linkType: hard + +"get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.4": + version: 1.2.4 + resolution: "get-intrinsic@npm:1.2.4" + dependencies: + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + has-proto: "npm:^1.0.1" + has-symbols: "npm:^1.0.3" + hasown: "npm:^2.0.0" + checksum: 10c0/0a9b82c16696ed6da5e39b1267104475c47e3a9bdbe8b509dfe1710946e38a87be70d759f4bb3cda042d76a41ef47fe769660f3b7c0d1f68750299344ffb15b7 + languageName: node + linkType: hard + +"get-pkg-repo@npm:^4.2.1": + version: 4.2.1 + resolution: "get-pkg-repo@npm:4.2.1" + dependencies: + "@hutson/parse-repository-url": "npm:^3.0.0" + hosted-git-info: "npm:^4.0.0" + through2: "npm:^2.0.0" + yargs: "npm:^16.2.0" + bin: + get-pkg-repo: src/cli.js + checksum: 10c0/1338d2e048a594da4a34e7dd69d909376d72784f5ba50963a242b4b35db77533786f618b3f6a9effdee2af20af4917a3b7cf12533b4575d7f9c163886be1fb62 + languageName: node + linkType: hard + +"get-port@npm:5.1.1": + version: 5.1.1 + resolution: "get-port@npm:5.1.1" + checksum: 10c0/2873877a469b24e6d5e0be490724a17edb39fafc795d1d662e7bea951ca649713b4a50117a473f9d162312cb0e946597bd0e049ed2f866e79e576e8e213d3d1c + languageName: node + linkType: hard + +"get-stream@npm:6.0.0": + version: 6.0.0 + resolution: "get-stream@npm:6.0.0" + checksum: 10c0/7cd835cb9180041e7be2cc3de236e5db9f2144515921aeb60ae78d3a46f9944439d654c2aae5b0191e41eb6e2500f0237494a2e6c0790367183f788d1c9f6dd6 + languageName: node + linkType: hard + +"get-stream@npm:^6.0.0": + version: 6.0.1 + resolution: "get-stream@npm:6.0.1" + checksum: 10c0/49825d57d3fd6964228e6200a58169464b8e8970489b3acdc24906c782fb7f01f9f56f8e6653c4a50713771d6658f7cfe051e5eb8c12e334138c9c918b296341 + languageName: node + linkType: hard + +"git-raw-commits@npm:^3.0.0": + version: 3.0.0 + resolution: "git-raw-commits@npm:3.0.0" + dependencies: + dargs: "npm:^7.0.0" + meow: "npm:^8.1.2" + split2: "npm:^3.2.2" + bin: + git-raw-commits: cli.js + checksum: 10c0/2a5db2e4b5b1ef7b6ecbdc175e559920a5400cbdb8d36f130aaef3588bfd74d8650b354a51ff89e0929eadbb265a00078a6291ff26248a525f0b2f079b001bf6 + languageName: node + linkType: hard + +"git-remote-origin-url@npm:^2.0.0": + version: 2.0.0 + resolution: "git-remote-origin-url@npm:2.0.0" + dependencies: + gitconfiglocal: "npm:^1.0.0" + pify: "npm:^2.3.0" + checksum: 10c0/3a846ce98ed36b2d0b801e8ec1ab299a236cfc6fa264bfdf9f42301abfdfd8715c946507fd83a10b9db449eb609ac6f8a2a341daf52e3af0000367487f486355 + languageName: node + linkType: hard + +"git-semver-tags@npm:^5.0.0": + version: 5.0.1 + resolution: "git-semver-tags@npm:5.0.1" + dependencies: + meow: "npm:^8.1.2" + semver: "npm:^7.0.0" + bin: + git-semver-tags: cli.js + checksum: 10c0/7cacba2f4ac19c0ccb8e6bb7301409376e5a2cc178692667afff453e6fe81f79b5f3f5040343e2be127a2f34977528d354de2aa32430917e90b64884debd3102 + languageName: node + linkType: hard + +"git-up@npm:^7.0.0": + version: 7.0.0 + resolution: "git-up@npm:7.0.0" + dependencies: + is-ssh: "npm:^1.4.0" + parse-url: "npm:^8.1.0" + checksum: 10c0/a3fa02e1a63c7c824b5ebbf23f4a9a6b34dd80031114c5dd8adb7ef53493642e39d3d80dfef4025a452128400c35c2c138d20a0f6ae5d7d7ef70d9ba13083d34 + languageName: node + linkType: hard + +"git-url-parse@npm:14.0.0": + version: 14.0.0 + resolution: "git-url-parse@npm:14.0.0" + dependencies: + git-up: "npm:^7.0.0" + checksum: 10c0/d360cf23c6278e302b74603f3dc490c3fe22e533d58b7f35e0295fad9af209ce5046a55950ccbf2f0d18de7931faefb4353e3f3fd3dda87fce77b409d48e0ba9 + languageName: node + linkType: hard + +"gitconfiglocal@npm:^1.0.0": + version: 1.0.0 + resolution: "gitconfiglocal@npm:1.0.0" + dependencies: + ini: "npm:^1.3.2" + checksum: 10c0/cfcb16344834113199f209f2758ced778dc30e075ddb49b5dde659b4dd2deadee824db0a1b77e1303cb594d9e8b2240da18c67705f657aa76affb444aa349005 + languageName: node + linkType: hard + +"glob-parent@npm:6.0.2, glob-parent@npm:^6.0.2": + version: 6.0.2 + resolution: "glob-parent@npm:6.0.2" + dependencies: + is-glob: "npm:^4.0.3" + checksum: 10c0/317034d88654730230b3f43bb7ad4f7c90257a426e872ea0bf157473ac61c99bf5d205fad8f0185f989be8d2fa6d3c7dce1645d99d545b6ea9089c39f838e7f8 + languageName: node + linkType: hard + +"glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2": + version: 5.1.2 + resolution: "glob-parent@npm:5.1.2" + dependencies: + is-glob: "npm:^4.0.1" + checksum: 10c0/cab87638e2112bee3f839ef5f6e0765057163d39c66be8ec1602f3823da4692297ad4e972de876ea17c44d652978638d2fd583c6713d0eb6591706825020c9ee + languageName: node + linkType: hard + +"glob@npm:^10.2.2, glob@npm:^10.3.10, glob@npm:^10.3.3, glob@npm:^10.3.7": + version: 10.4.5 + resolution: "glob@npm:10.4.5" + dependencies: + foreground-child: "npm:^3.1.0" + jackspeak: "npm:^3.1.2" + minimatch: "npm:^9.0.4" + minipass: "npm:^7.1.2" + package-json-from-dist: "npm:^1.0.0" + path-scurry: "npm:^1.11.1" + bin: + glob: dist/esm/bin.mjs + checksum: 10c0/19a9759ea77b8e3ca0a43c2f07ecddc2ad46216b786bb8f993c445aee80d345925a21e5280c7b7c6c59e860a0154b84e4b2b60321fea92cd3c56b4a7489f160e + languageName: node + linkType: hard + +"glob@npm:^7.1.3": + version: 7.2.3 + resolution: "glob@npm:7.2.3" + dependencies: + fs.realpath: "npm:^1.0.0" + inflight: "npm:^1.0.4" + inherits: "npm:2" + minimatch: "npm:^3.1.1" + once: "npm:^1.3.0" + path-is-absolute: "npm:^1.0.0" + checksum: 10c0/65676153e2b0c9095100fe7f25a778bf45608eeb32c6048cf307f579649bcc30353277b3b898a3792602c65764e5baa4f643714dfbdfd64ea271d210c7a425fe + languageName: node + linkType: hard + +"glob@npm:^9.2.0": + version: 9.3.5 + resolution: "glob@npm:9.3.5" + dependencies: + fs.realpath: "npm:^1.0.0" + minimatch: "npm:^8.0.2" + minipass: "npm:^4.2.4" + path-scurry: "npm:^1.6.1" + checksum: 10c0/2f6c2b9ee019ee21dc258ae97a88719614591e4c979cb4580b1b9df6f0f778a3cb38b4bdaf18dfa584637ea10f89a3c5f2533a5e449cf8741514ad18b0951f2e + languageName: node + linkType: hard + +"globals@npm:^11.1.0": + version: 11.12.0 + resolution: "globals@npm:11.12.0" + checksum: 10c0/758f9f258e7b19226bd8d4af5d3b0dcf7038780fb23d82e6f98932c44e239f884847f1766e8fa9cc5635ccb3204f7fa7314d4408dd4002a5e8ea827b4018f0a1 + languageName: node + linkType: hard + +"globby@npm:11.1.0": + version: 11.1.0 + resolution: "globby@npm:11.1.0" + dependencies: + array-union: "npm:^2.1.0" + dir-glob: "npm:^3.0.1" + fast-glob: "npm:^3.2.9" + ignore: "npm:^5.2.0" + merge2: "npm:^1.4.1" + slash: "npm:^3.0.0" + checksum: 10c0/b39511b4afe4bd8a7aead3a27c4ade2b9968649abab0a6c28b1a90141b96ca68ca5db1302f7c7bd29eab66bf51e13916b8e0a3d0ac08f75e1e84a39b35691189 + languageName: node + linkType: hard + +"gopd@npm:^1.0.1": + version: 1.0.1 + resolution: "gopd@npm:1.0.1" + dependencies: + get-intrinsic: "npm:^1.1.3" + checksum: 10c0/505c05487f7944c552cee72087bf1567debb470d4355b1335f2c262d218ebbff805cd3715448fe29b4b380bae6912561d0467233e4165830efd28da241418c63 + languageName: node + linkType: hard + +"graceful-fs@npm:4.2.11, graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.15, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.11, graceful-fs@npm:^4.2.6": + version: 4.2.11 + resolution: "graceful-fs@npm:4.2.11" + checksum: 10c0/386d011a553e02bc594ac2ca0bd6d9e4c22d7fa8cfbfc448a6d148c59ea881b092db9dbe3547ae4b88e55f1b01f7c4a2ecc53b310c042793e63aa44cf6c257f2 + languageName: node + linkType: hard + +"handlebars@npm:^4.7.7": + version: 4.7.8 + resolution: "handlebars@npm:4.7.8" + dependencies: + minimist: "npm:^1.2.5" + neo-async: "npm:^2.6.2" + source-map: "npm:^0.6.1" + uglify-js: "npm:^3.1.4" + wordwrap: "npm:^1.0.0" + dependenciesMeta: + uglify-js: + optional: true + bin: + handlebars: bin/handlebars + checksum: 10c0/7aff423ea38a14bb379316f3857fe0df3c5d66119270944247f155ba1f08e07a92b340c58edaa00cfe985c21508870ee5183e0634dcb53dd405f35c93ef7f10d + languageName: node + linkType: hard + +"hard-rejection@npm:^2.1.0": + version: 2.1.0 + resolution: "hard-rejection@npm:2.1.0" + checksum: 10c0/febc3343a1ad575aedcc112580835b44a89a89e01f400b4eda6e8110869edfdab0b00cd1bd4c3bfec9475a57e79e0b355aecd5be46454b6a62b9a359af60e564 + languageName: node + linkType: hard + +"has-flag@npm:^3.0.0": + version: 3.0.0 + resolution: "has-flag@npm:3.0.0" + checksum: 10c0/1c6c83b14b8b1b3c25b0727b8ba3e3b647f99e9e6e13eb7322107261de07a4c1be56fc0d45678fc376e09772a3a1642ccdaf8fc69bdf123b6c086598397ce473 + languageName: node + linkType: hard + +"has-flag@npm:^4.0.0": + version: 4.0.0 + resolution: "has-flag@npm:4.0.0" + checksum: 10c0/2e789c61b7888d66993e14e8331449e525ef42aac53c627cc53d1c3334e768bcb6abdc4f5f0de1478a25beec6f0bd62c7549058b7ac53e924040d4f301f02fd1 + languageName: node + linkType: hard + +"has-property-descriptors@npm:^1.0.2": + version: 1.0.2 + resolution: "has-property-descriptors@npm:1.0.2" + dependencies: + es-define-property: "npm:^1.0.0" + checksum: 10c0/253c1f59e80bb476cf0dde8ff5284505d90c3bdb762983c3514d36414290475fe3fd6f574929d84de2a8eec00d35cf07cb6776205ff32efd7c50719125f00236 + languageName: node + linkType: hard + +"has-proto@npm:^1.0.1": + version: 1.0.3 + resolution: "has-proto@npm:1.0.3" + checksum: 10c0/35a6989f81e9f8022c2f4027f8b48a552de714938765d019dbea6bb547bd49ce5010a3c7c32ec6ddac6e48fc546166a3583b128f5a7add8b058a6d8b4afec205 + languageName: node + linkType: hard + +"has-symbols@npm:^1.0.3": + version: 1.0.3 + resolution: "has-symbols@npm:1.0.3" + checksum: 10c0/e6922b4345a3f37069cdfe8600febbca791c94988c01af3394d86ca3360b4b93928bbf395859158f88099cb10b19d98e3bbab7c9ff2c1bd09cf665ee90afa2c3 + languageName: node + linkType: hard + +"has-unicode@npm:2.0.1, has-unicode@npm:^2.0.1": + version: 2.0.1 + resolution: "has-unicode@npm:2.0.1" + checksum: 10c0/ebdb2f4895c26bb08a8a100b62d362e49b2190bcfd84b76bc4be1a3bd4d254ec52d0dd9f2fbcc093fc5eb878b20c52146f9dfd33e2686ed28982187be593b47c + languageName: node + linkType: hard + +"hasown@npm:^2.0.0, hasown@npm:^2.0.2": + version: 2.0.2 + resolution: "hasown@npm:2.0.2" + dependencies: + function-bind: "npm:^1.1.2" + checksum: 10c0/3769d434703b8ac66b209a4cca0737519925bbdb61dd887f93a16372b14694c63ff4e797686d87c90f08168e81082248b9b028bad60d4da9e0d1148766f56eb9 + languageName: node + linkType: hard + +"hast-util-parse-selector@npm:^2.0.0": + version: 2.2.5 + resolution: "hast-util-parse-selector@npm:2.2.5" + checksum: 10c0/29b7ee77960ded6a99d30c287d922243071cc07b39f2006f203bd08ee54eb8f66bdaa86ef6527477c766e2382d520b60ee4e4087f189888c35d8bcc020173648 + languageName: node + linkType: hard + +"hastscript@npm:^6.0.0": + version: 6.0.0 + resolution: "hastscript@npm:6.0.0" + dependencies: + "@types/hast": "npm:^2.0.0" + comma-separated-tokens: "npm:^1.0.0" + hast-util-parse-selector: "npm:^2.0.0" + property-information: "npm:^5.0.0" + space-separated-tokens: "npm:^1.0.0" + checksum: 10c0/f76d9cf373cb075c8523c8ad52709f09f7e02b7c9d3152b8d35c65c265b9f1878bed6023f215a7d16523921036d40a7da292cb6f4399af9b5eccac2a5a5eb330 + languageName: node + linkType: hard + +"he@npm:^1.2.0": + version: 1.2.0 + resolution: "he@npm:1.2.0" + bin: + he: bin/he + checksum: 10c0/a27d478befe3c8192f006cdd0639a66798979dfa6e2125c6ac582a19a5ebfec62ad83e8382e6036170d873f46e4536a7e795bf8b95bf7c247f4cc0825ccc8c17 + languageName: node + linkType: hard + +"helmet@npm:^7.1.0": + version: 7.1.0 + resolution: "helmet@npm:7.1.0" + checksum: 10c0/8c3370d07487be11ac918577c68952e05d779a1a2c037023c1ba763034c381a025899bc52f8acfab5209304a1dc618a3764dbfd26386a0d1173befe4fb932e84 + languageName: node + linkType: hard + +"highlight.js@npm:^10.4.1, highlight.js@npm:~10.7.0": + version: 10.7.3 + resolution: "highlight.js@npm:10.7.3" + checksum: 10c0/073837eaf816922427a9005c56c42ad8786473dc042332dfe7901aa065e92bc3d94ebf704975257526482066abb2c8677cc0326559bb8621e046c21c5991c434 + languageName: node + linkType: hard + +"hosted-git-info@npm:^2.1.4": + version: 2.8.9 + resolution: "hosted-git-info@npm:2.8.9" + checksum: 10c0/317cbc6b1bbbe23c2a40ae23f3dafe9fa349ce42a89a36f930e3f9c0530c179a3882d2ef1e4141a4c3674d6faaea862138ec55b43ad6f75e387fda2483a13c70 + languageName: node + linkType: hard + +"hosted-git-info@npm:^4.0.0, hosted-git-info@npm:^4.0.1": + version: 4.1.0 + resolution: "hosted-git-info@npm:4.1.0" + dependencies: + lru-cache: "npm:^6.0.0" + checksum: 10c0/150fbcb001600336d17fdbae803264abed013548eea7946c2264c49ebe2ebd8c4441ba71dd23dd8e18c65de79d637f98b22d4760ba5fb2e0b15d62543d0fff07 + languageName: node + linkType: hard + +"hosted-git-info@npm:^7.0.0, hosted-git-info@npm:^7.0.2": + version: 7.0.2 + resolution: "hosted-git-info@npm:7.0.2" + dependencies: + lru-cache: "npm:^10.0.1" + checksum: 10c0/b19dbd92d3c0b4b0f1513cf79b0fc189f54d6af2129eeb201de2e9baaa711f1936929c848b866d9c8667a0f956f34bf4f07418c12be1ee9ca74fd9246335ca1f + languageName: node + linkType: hard + +"html-minifier@npm:^4.0.0": + version: 4.0.0 + resolution: "html-minifier@npm:4.0.0" + dependencies: + camel-case: "npm:^3.0.0" + clean-css: "npm:^4.2.1" + commander: "npm:^2.19.0" + he: "npm:^1.2.0" + param-case: "npm:^2.1.1" + relateurl: "npm:^0.2.7" + uglify-js: "npm:^3.5.1" + bin: + html-minifier: ./cli.js + checksum: 10c0/38c2d1cab49593671b104e3bc120b1c5fdf00c75930fcb32e257322219c9d31515af6b39af76e8ecd71fdf3a77d168f8b7b7ce02beba0b72eb5631599b5561bc + languageName: node + linkType: hard + +"htmlparser2@npm:^5.0.0": + version: 5.0.1 + resolution: "htmlparser2@npm:5.0.1" + dependencies: + domelementtype: "npm:^2.0.1" + domhandler: "npm:^3.3.0" + domutils: "npm:^2.4.2" + entities: "npm:^2.0.0" + checksum: 10c0/3f276f7ac518930f5330cfe5129dd5764a63e9bae6f57350e90b26affc94b11b2fb6750f056fed245b726d500e78197b4a09c7108c71964fe91303e6e2a29107 + languageName: node + linkType: hard + +"htmlparser2@npm:^8.0.1": + version: 8.0.2 + resolution: "htmlparser2@npm:8.0.2" + dependencies: + domelementtype: "npm:^2.3.0" + domhandler: "npm:^5.0.3" + domutils: "npm:^3.0.1" + entities: "npm:^4.4.0" + checksum: 10c0/609cca85886d0bf2c9a5db8c6926a89f3764596877492e2caa7a25a789af4065bc6ee2cdc81807fe6b1d03a87bf8a373b5a754528a4cc05146b713c20575aab4 + languageName: node + linkType: hard + +"htmlparser2@npm:^9.1.0": + version: 9.1.0 + resolution: "htmlparser2@npm:9.1.0" + dependencies: + domelementtype: "npm:^2.3.0" + domhandler: "npm:^5.0.3" + domutils: "npm:^3.1.0" + entities: "npm:^4.5.0" + checksum: 10c0/394f6323efc265bbc791d8c0d96bfe95984e0407565248521ab92e2dc7668e5ceeca7bc6ed18d408b9ee3b25032c5743368a4280d280332d782821d5d467ad8f + languageName: node + linkType: hard + +"http-cache-semantics@npm:^4.1.1": + version: 4.1.1 + resolution: "http-cache-semantics@npm:4.1.1" + checksum: 10c0/ce1319b8a382eb3cbb4a37c19f6bfe14e5bb5be3d09079e885e8c513ab2d3cd9214902f8a31c9dc4e37022633ceabfc2d697405deeaf1b8f3552bb4ed996fdfc + languageName: node + linkType: hard + +"http-errors@npm:2.0.0": + version: 2.0.0 + resolution: "http-errors@npm:2.0.0" + dependencies: + depd: "npm:2.0.0" + inherits: "npm:2.0.4" + setprototypeof: "npm:1.2.0" + statuses: "npm:2.0.1" + toidentifier: "npm:1.0.1" + checksum: 10c0/fc6f2715fe188d091274b5ffc8b3657bd85c63e969daa68ccb77afb05b071a4b62841acb7a21e417b5539014dff2ebf9550f0b14a9ff126f2734a7c1387f8e19 + languageName: node + linkType: hard + +"http-proxy-agent@npm:^7.0.0": + version: 7.0.2 + resolution: "http-proxy-agent@npm:7.0.2" + dependencies: + agent-base: "npm:^7.1.0" + debug: "npm:^4.3.4" + checksum: 10c0/4207b06a4580fb85dd6dff521f0abf6db517489e70863dca1a0291daa7f2d3d2d6015a57bd702af068ea5cf9f1f6ff72314f5f5b4228d299c0904135d2aef921 + languageName: node + linkType: hard + +"https-proxy-agent@npm:^5.0.0": + version: 5.0.1 + resolution: "https-proxy-agent@npm:5.0.1" + dependencies: + agent-base: "npm:6" + debug: "npm:4" + checksum: 10c0/6dd639f03434003577c62b27cafdb864784ef19b2de430d8ae2a1d45e31c4fd60719e5637b44db1a88a046934307da7089e03d6089ec3ddacc1189d8de8897d1 + languageName: node + linkType: hard + +"https-proxy-agent@npm:^7.0.1": + version: 7.0.5 + resolution: "https-proxy-agent@npm:7.0.5" + dependencies: + agent-base: "npm:^7.0.2" + debug: "npm:4" + checksum: 10c0/2490e3acec397abeb88807db52cac59102d5ed758feee6df6112ab3ccd8325e8a1ce8bce6f4b66e5470eca102d31e425ace904242e4fa28dbe0c59c4bafa7b2c + languageName: node + linkType: hard + +"human-signals@npm:^2.1.0": + version: 2.1.0 + resolution: "human-signals@npm:2.1.0" + checksum: 10c0/695edb3edfcfe9c8b52a76926cd31b36978782062c0ed9b1192b36bebc75c4c87c82e178dfcb0ed0fc27ca59d434198aac0bd0be18f5781ded775604db22304a + languageName: node + linkType: hard + +"iconv-lite@npm:0.4.24, iconv-lite@npm:^0.4.24": + version: 0.4.24 + resolution: "iconv-lite@npm:0.4.24" + dependencies: + safer-buffer: "npm:>= 2.1.2 < 3" + checksum: 10c0/c6886a24cc00f2a059767440ec1bc00d334a89f250db8e0f7feb4961c8727118457e27c495ba94d082e51d3baca378726cd110aaf7ded8b9bbfd6a44760cf1d4 + languageName: node + linkType: hard + +"iconv-lite@npm:^0.6.2": + version: 0.6.3 + resolution: "iconv-lite@npm:0.6.3" + dependencies: + safer-buffer: "npm:>= 2.1.2 < 3.0.0" + checksum: 10c0/98102bc66b33fcf5ac044099d1257ba0b7ad5e3ccd3221f34dd508ab4070edff183276221684e1e0555b145fce0850c9f7d2b60a9fcac50fbb4ea0d6e845a3b1 + languageName: node + linkType: hard + +"ieee754@npm:^1.1.13": + version: 1.2.1 + resolution: "ieee754@npm:1.2.1" + checksum: 10c0/b0782ef5e0935b9f12883a2e2aa37baa75da6e66ce6515c168697b42160807d9330de9a32ec1ed73149aea02e0d822e572bca6f1e22bdcbd2149e13b050b17bb + languageName: node + linkType: hard + +"ignore-walk@npm:^6.0.4": + version: 6.0.5 + resolution: "ignore-walk@npm:6.0.5" + dependencies: + minimatch: "npm:^9.0.0" + checksum: 10c0/8bd6d37c82400016c7b6538b03422dde8c9d7d3e99051c8357dd205d499d42828522fb4fbce219c9c21b4b069079445bacdc42bbd3e2e073b52856c2646d8a39 + languageName: node + linkType: hard + +"ignore@npm:^5.0.4, ignore@npm:^5.2.0": + version: 5.3.1 + resolution: "ignore@npm:5.3.1" + checksum: 10c0/703f7f45ffb2a27fb2c5a8db0c32e7dee66b33a225d28e8db4e1be6474795f606686a6e3bcc50e1aa12f2042db4c9d4a7d60af3250511de74620fbed052ea4cd + languageName: node + linkType: hard + +"import-fresh@npm:^3.3.0": + version: 3.3.0 + resolution: "import-fresh@npm:3.3.0" + dependencies: + parent-module: "npm:^1.0.0" + resolve-from: "npm:^4.0.0" + checksum: 10c0/7f882953aa6b740d1f0e384d0547158bc86efbf2eea0f1483b8900a6f65c5a5123c2cf09b0d542cc419d0b98a759ecaeb394237e97ea427f2da221dc3cd80cc3 + languageName: node + linkType: hard + +"import-local@npm:3.1.0": + version: 3.1.0 + resolution: "import-local@npm:3.1.0" + dependencies: + pkg-dir: "npm:^4.2.0" + resolve-cwd: "npm:^3.0.0" + bin: + import-local-fixture: fixtures/cli.js + checksum: 10c0/c67ecea72f775fe8684ca3d057e54bdb2ae28c14bf261d2607c269c18ea0da7b730924c06262eca9aed4b8ab31e31d65bc60b50e7296c85908a56e2f7d41ecd2 + languageName: node + linkType: hard + +"imurmurhash@npm:^0.1.4": + version: 0.1.4 + resolution: "imurmurhash@npm:0.1.4" + checksum: 10c0/8b51313850dd33605c6c9d3fd9638b714f4c4c40250cff658209f30d40da60f78992fb2df5dabee4acf589a6a82bbc79ad5486550754bd9ec4e3fc0d4a57d6a6 + languageName: node + linkType: hard + +"indent-string@npm:^4.0.0": + version: 4.0.0 + resolution: "indent-string@npm:4.0.0" + checksum: 10c0/1e1904ddb0cb3d6cce7cd09e27a90184908b7a5d5c21b92e232c93579d314f0b83c246ffb035493d0504b1e9147ba2c9b21df0030f48673fba0496ecd698161f + languageName: node + linkType: hard + +"inflight@npm:^1.0.4": + version: 1.0.6 + resolution: "inflight@npm:1.0.6" + dependencies: + once: "npm:^1.3.0" + wrappy: "npm:1" + checksum: 10c0/7faca22584600a9dc5b9fca2cd5feb7135ac8c935449837b315676b4c90aa4f391ec4f42240178244b5a34e8bede1948627fda392ca3191522fc46b34e985ab2 + languageName: node + linkType: hard + +"inherits@npm:2, inherits@npm:2.0.4, inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.3": + version: 2.0.4 + resolution: "inherits@npm:2.0.4" + checksum: 10c0/4e531f648b29039fb7426fb94075e6545faa1eb9fe83c29f0b6d9e7263aceb4289d2d4557db0d428188eeb449cc7c5e77b0a0b2c4e248ff2a65933a0dee49ef2 + languageName: node + linkType: hard + +"ini@npm:^1.3.2, ini@npm:^1.3.4, ini@npm:^1.3.8": + version: 1.3.8 + resolution: "ini@npm:1.3.8" + checksum: 10c0/ec93838d2328b619532e4f1ff05df7909760b6f66d9c9e2ded11e5c1897d6f2f9980c54dd638f88654b00919ce31e827040631eab0a3969e4d1abefa0719516a + languageName: node + linkType: hard + +"ini@npm:^4.1.3": + version: 4.1.3 + resolution: "ini@npm:4.1.3" + checksum: 10c0/0d27eff094d5f3899dd7c00d0c04ea733ca03a8eb6f9406ce15daac1a81de022cb417d6eaff7e4342451ffa663389c565ffc68d6825eaf686bf003280b945764 + languageName: node + linkType: hard + +"init-package-json@npm:6.0.3": + version: 6.0.3 + resolution: "init-package-json@npm:6.0.3" + dependencies: + "@npmcli/package-json": "npm:^5.0.0" + npm-package-arg: "npm:^11.0.0" + promzard: "npm:^1.0.0" + read: "npm:^3.0.1" + semver: "npm:^7.3.5" + validate-npm-package-license: "npm:^3.0.4" + validate-npm-package-name: "npm:^5.0.0" + checksum: 10c0/a80f024ee041a2cf4d3062ba936abf015cbc32bda625cabe994d1fa4bd942bb9af37a481afd6880d340d3e94d90bf97bed1a0a877cc8c7c9b48e723c2524ae74 + languageName: node + linkType: hard + +"inquirer@npm:^8.2.4": + version: 8.2.6 + resolution: "inquirer@npm:8.2.6" + dependencies: + ansi-escapes: "npm:^4.2.1" + chalk: "npm:^4.1.1" + cli-cursor: "npm:^3.1.0" + cli-width: "npm:^3.0.0" + external-editor: "npm:^3.0.3" + figures: "npm:^3.0.0" + lodash: "npm:^4.17.21" + mute-stream: "npm:0.0.8" + ora: "npm:^5.4.1" + run-async: "npm:^2.4.0" + rxjs: "npm:^7.5.5" + string-width: "npm:^4.1.0" + strip-ansi: "npm:^6.0.0" + through: "npm:^2.3.6" + wrap-ansi: "npm:^6.0.1" + checksum: 10c0/eb5724de1778265323f3a68c80acfa899378cb43c24cdcb58661386500e5696b6b0b6c700e046b7aa767fe7b4823c6f04e6ddc268173e3f84116112529016296 + languageName: node + linkType: hard + +"internmap@npm:1 - 2": + version: 2.0.3 + resolution: "internmap@npm:2.0.3" + checksum: 10c0/8cedd57f07bbc22501516fbfc70447f0c6812871d471096fad9ea603516eacc2137b633633daf432c029712df0baefd793686388ddf5737e3ea15074b877f7ed + languageName: node + linkType: hard + +"ioredis@npm:*, ioredis@npm:^5.4.1": + version: 5.4.1 + resolution: "ioredis@npm:5.4.1" + dependencies: + "@ioredis/commands": "npm:^1.1.1" + cluster-key-slot: "npm:^1.1.0" + debug: "npm:^4.3.4" + denque: "npm:^2.1.0" + lodash.defaults: "npm:^4.2.0" + lodash.isarguments: "npm:^3.1.0" + redis-errors: "npm:^1.2.0" + redis-parser: "npm:^3.0.0" + standard-as-callback: "npm:^2.1.0" + checksum: 10c0/5d28b7c89a3cab5b76d75923d7d4ce79172b3a1ca9be690133f6e8e393a7a4b4ffd55513e618bbb5504fed80d9e1395c9d9531a7c5c5c84aa4c4e765cca75456 + languageName: node + linkType: hard + +"ip-address@npm:^9.0.5": + version: 9.0.5 + resolution: "ip-address@npm:9.0.5" + dependencies: + jsbn: "npm:1.1.0" + sprintf-js: "npm:^1.1.3" + checksum: 10c0/331cd07fafcb3b24100613e4b53e1a2b4feab11e671e655d46dc09ee233da5011284d09ca40c4ecbdfe1d0004f462958675c224a804259f2f78d2465a87824bc + languageName: node + linkType: hard + +"ipaddr.js@npm:1.9.1": + version: 1.9.1 + resolution: "ipaddr.js@npm:1.9.1" + checksum: 10c0/0486e775047971d3fdb5fb4f063829bac45af299ae0b82dcf3afa2145338e08290563a2a70f34b732d795ecc8311902e541a8530eeb30d75860a78ff4e94ce2a + languageName: node + linkType: hard + +"is-alphabetical@npm:^1.0.0": + version: 1.0.4 + resolution: "is-alphabetical@npm:1.0.4" + checksum: 10c0/1505b1de5a1fd74022c05fb21b0e683a8f5229366bac8dc4d34cf6935bcfd104d1125a5e6b083fb778847629f76e5bdac538de5367bdf2b927a1356164e23985 + languageName: node + linkType: hard + +"is-alphanumerical@npm:^1.0.0": + version: 1.0.4 + resolution: "is-alphanumerical@npm:1.0.4" + dependencies: + is-alphabetical: "npm:^1.0.0" + is-decimal: "npm:^1.0.0" + checksum: 10c0/d623abae7130a7015c6bf33d99151d4e7005572fd170b86568ff4de5ae86ac7096608b87dd4a1d4dbbd497e392b6396930ba76c9297a69455909cebb68005905 + languageName: node + linkType: hard + +"is-arrayish@npm:^0.2.1": + version: 0.2.1 + resolution: "is-arrayish@npm:0.2.1" + checksum: 10c0/e7fb686a739068bb70f860b39b67afc62acc62e36bb61c5f965768abce1873b379c563e61dd2adad96ebb7edf6651111b385e490cf508378959b0ed4cac4e729 + languageName: node + linkType: hard + +"is-arrayish@npm:^0.3.1": + version: 0.3.2 + resolution: "is-arrayish@npm:0.3.2" + checksum: 10c0/f59b43dc1d129edb6f0e282595e56477f98c40278a2acdc8b0a5c57097c9eff8fe55470493df5775478cf32a4dc8eaf6d3a749f07ceee5bc263a78b2434f6a54 + languageName: node + linkType: hard + +"is-binary-path@npm:~2.1.0": + version: 2.1.0 + resolution: "is-binary-path@npm:2.1.0" + dependencies: + binary-extensions: "npm:^2.0.0" + checksum: 10c0/a16eaee59ae2b315ba36fad5c5dcaf8e49c3e27318f8ab8fa3cdb8772bf559c8d1ba750a589c2ccb096113bb64497084361a25960899cb6172a6925ab6123d38 + languageName: node + linkType: hard + +"is-ci@npm:3.0.1": + version: 3.0.1 + resolution: "is-ci@npm:3.0.1" + dependencies: + ci-info: "npm:^3.2.0" + bin: + is-ci: bin.js + checksum: 10c0/0e81caa62f4520d4088a5bef6d6337d773828a88610346c4b1119fb50c842587ed8bef1e5d9a656835a599e7209405b5761ddf2339668f2d0f4e889a92fe6051 + languageName: node + linkType: hard + +"is-core-module@npm:^2.13.0, is-core-module@npm:^2.5.0": + version: 2.15.0 + resolution: "is-core-module@npm:2.15.0" + dependencies: + hasown: "npm:^2.0.2" + checksum: 10c0/da161f3d9906f459486da65609b2f1a2dfdc60887c689c234d04e88a062cb7920fa5be5fb7ab08dc43b732929653c4135ef05bf77888ae2a9040ce76815eb7b1 + languageName: node + linkType: hard + +"is-decimal@npm:^1.0.0": + version: 1.0.4 + resolution: "is-decimal@npm:1.0.4" + checksum: 10c0/a4ad53c4c5c4f5a12214e7053b10326711f6a71f0c63ba1314a77bd71df566b778e4ebd29f9fb6815f07a4dc50c3767fb19bd6fc9fa05e601410f1d64ffeac48 + languageName: node + linkType: hard + +"is-docker@npm:^2.0.0, is-docker@npm:^2.1.1": + version: 2.2.1 + resolution: "is-docker@npm:2.2.1" + bin: + is-docker: cli.js + checksum: 10c0/e828365958d155f90c409cdbe958f64051d99e8aedc2c8c4cd7c89dcf35329daed42f7b99346f7828df013e27deb8f721cf9408ba878c76eb9e8290235fbcdcc + languageName: node + linkType: hard + +"is-extglob@npm:^2.1.1": + version: 2.1.1 + resolution: "is-extglob@npm:2.1.1" + checksum: 10c0/5487da35691fbc339700bbb2730430b07777a3c21b9ebaecb3072512dfd7b4ba78ac2381a87e8d78d20ea08affb3f1971b4af629173a6bf435ff8a4c47747912 + languageName: node + linkType: hard + +"is-fullwidth-code-point@npm:^3.0.0": + version: 3.0.0 + resolution: "is-fullwidth-code-point@npm:3.0.0" + checksum: 10c0/bb11d825e049f38e04c06373a8d72782eee0205bda9d908cc550ccb3c59b99d750ff9537982e01733c1c94a58e35400661f57042158ff5e8f3e90cf936daf0fc + languageName: node + linkType: hard + +"is-glob@npm:^4.0.1, is-glob@npm:^4.0.3, is-glob@npm:~4.0.1": + version: 4.0.3 + resolution: "is-glob@npm:4.0.3" + dependencies: + is-extglob: "npm:^2.1.1" + checksum: 10c0/17fb4014e22be3bbecea9b2e3a76e9e34ff645466be702f1693e8f1ee1adac84710d0be0bd9f967d6354036fd51ab7c2741d954d6e91dae6bb69714de92c197a + languageName: node + linkType: hard + +"is-hexadecimal@npm:^1.0.0": + version: 1.0.4 + resolution: "is-hexadecimal@npm:1.0.4" + checksum: 10c0/ec4c64e5624c0f240922324bc697e166554f09d3ddc7633fc526084502626445d0a871fbd8cae52a9844e83bd0bb414193cc5a66806d7b2867907003fc70c5ea + languageName: node + linkType: hard + +"is-interactive@npm:^1.0.0": + version: 1.0.0 + resolution: "is-interactive@npm:1.0.0" + checksum: 10c0/dd47904dbf286cd20aa58c5192161be1a67138485b9836d5a70433b21a45442e9611b8498b8ab1f839fc962c7620667a50535fdfb4a6bc7989b8858645c06b4d + languageName: node + linkType: hard + +"is-lambda@npm:^1.0.1": + version: 1.0.1 + resolution: "is-lambda@npm:1.0.1" + checksum: 10c0/85fee098ae62ba6f1e24cf22678805473c7afd0fb3978a3aa260e354cb7bcb3a5806cf0a98403188465efedec41ab4348e8e4e79305d409601323855b3839d4d + languageName: node + linkType: hard + +"is-number@npm:^7.0.0": + version: 7.0.0 + resolution: "is-number@npm:7.0.0" + checksum: 10c0/b4686d0d3053146095ccd45346461bc8e53b80aeb7671cc52a4de02dbbf7dc0d1d2a986e2fe4ae206984b4d34ef37e8b795ebc4f4295c978373e6575e295d811 + languageName: node + linkType: hard + +"is-obj@npm:^2.0.0": + version: 2.0.0 + resolution: "is-obj@npm:2.0.0" + checksum: 10c0/85044ed7ba8bd169e2c2af3a178cacb92a97aa75de9569d02efef7f443a824b5e153eba72b9ae3aca6f8ce81955271aa2dc7da67a8b720575d3e38104208cb4e + languageName: node + linkType: hard + +"is-plain-obj@npm:^1.0.0, is-plain-obj@npm:^1.1.0": + version: 1.1.0 + resolution: "is-plain-obj@npm:1.1.0" + checksum: 10c0/daaee1805add26f781b413fdf192fc91d52409583be30ace35c82607d440da63cc4cac0ac55136716688d6c0a2c6ef3edb2254fecbd1fe06056d6bd15975ee8c + languageName: node + linkType: hard + +"is-plain-object@npm:^2.0.4": + version: 2.0.4 + resolution: "is-plain-object@npm:2.0.4" + dependencies: + isobject: "npm:^3.0.1" + checksum: 10c0/f050fdd5203d9c81e8c4df1b3ff461c4bc64e8b5ca383bcdde46131361d0a678e80bcf00b5257646f6c636197629644d53bd8e2375aea633de09a82d57e942f4 + languageName: node + linkType: hard + +"is-plain-object@npm:^5.0.0": + version: 5.0.0 + resolution: "is-plain-object@npm:5.0.0" + checksum: 10c0/893e42bad832aae3511c71fd61c0bf61aa3a6d853061c62a307261842727d0d25f761ce9379f7ba7226d6179db2a3157efa918e7fe26360f3bf0842d9f28942c + languageName: node + linkType: hard + +"is-ssh@npm:^1.4.0": + version: 1.4.0 + resolution: "is-ssh@npm:1.4.0" + dependencies: + protocols: "npm:^2.0.1" + checksum: 10c0/3eb30d1bcb4507cd25562e7ac61a1c0aa31772134c67cec9c3afe6f4d57ec17e8c2892600a608e8e583f32f53f36465b8968c0305f2855cfbff95acfd049e113 + languageName: node + linkType: hard + +"is-stream@npm:2.0.0": + version: 2.0.0 + resolution: "is-stream@npm:2.0.0" + checksum: 10c0/687f6bbd2b995573d33e6b40b2cbc8b9186a751aa3151c23e6fd2c4ca352e323a6dc010b09103f89c9ca0bf5c8c38f3fa8b74d5d9acd1c44f1499874d7e844f9 + languageName: node + linkType: hard + +"is-stream@npm:^2.0.0": + version: 2.0.1 + resolution: "is-stream@npm:2.0.1" + checksum: 10c0/7c284241313fc6efc329b8d7f08e16c0efeb6baab1b4cd0ba579eb78e5af1aa5da11e68559896a2067cd6c526bd29241dda4eb1225e627d5aa1a89a76d4635a5 + languageName: node + linkType: hard + +"is-text-path@npm:^1.0.1": + version: 1.0.1 + resolution: "is-text-path@npm:1.0.1" + dependencies: + text-extensions: "npm:^1.0.0" + checksum: 10c0/61c8650c29548febb6bf69e9541fc11abbbb087a0568df7bc471ba264e95fb254def4e610631cbab4ddb0a1a07949d06416f4ebeaf37875023fb184cdb87ee84 + languageName: node + linkType: hard + +"is-unicode-supported@npm:^0.1.0": + version: 0.1.0 + resolution: "is-unicode-supported@npm:0.1.0" + checksum: 10c0/00cbe3455c3756be68d2542c416cab888aebd5012781d6819749fefb15162ff23e38501fe681b3d751c73e8ff561ac09a5293eba6f58fdf0178462ce6dcb3453 + languageName: node + linkType: hard + +"is-wsl@npm:^2.2.0": + version: 2.2.0 + resolution: "is-wsl@npm:2.2.0" + dependencies: + is-docker: "npm:^2.0.0" + checksum: 10c0/a6fa2d370d21be487c0165c7a440d567274fbba1a817f2f0bfa41cc5e3af25041d84267baa22df66696956038a43973e72fca117918c91431920bdef490fa25e + languageName: node + linkType: hard + +"isarray@npm:~1.0.0": + version: 1.0.0 + resolution: "isarray@npm:1.0.0" + checksum: 10c0/18b5be6669be53425f0b84098732670ed4e727e3af33bc7f948aac01782110eb9a18b3b329c5323bcdd3acdaae547ee077d3951317e7f133bff7105264b3003d + languageName: node + linkType: hard + +"isexe@npm:^2.0.0": + version: 2.0.0 + resolution: "isexe@npm:2.0.0" + checksum: 10c0/228cfa503fadc2c31596ab06ed6aa82c9976eec2bfd83397e7eaf06d0ccf42cd1dfd6743bf9aeb01aebd4156d009994c5f76ea898d2832c1fe342da923ca457d + languageName: node + linkType: hard + +"isexe@npm:^3.1.1": + version: 3.1.1 + resolution: "isexe@npm:3.1.1" + checksum: 10c0/9ec257654093443eb0a528a9c8cbba9c0ca7616ccb40abd6dde7202734d96bb86e4ac0d764f0f8cd965856aacbff2f4ce23e730dc19dfb41e3b0d865ca6fdcc7 + languageName: node + linkType: hard + +"isobject@npm:^3.0.1": + version: 3.0.1 + resolution: "isobject@npm:3.0.1" + checksum: 10c0/03344f5064a82f099a0cd1a8a407f4c0d20b7b8485e8e816c39f249e9416b06c322e8dec5b842b6bb8a06de0af9cb48e7bc1b5352f0fadc2f0abac033db3d4db + languageName: node + linkType: hard + +"jackspeak@npm:^3.1.2": + version: 3.4.3 + resolution: "jackspeak@npm:3.4.3" + dependencies: + "@isaacs/cliui": "npm:^8.0.2" + "@pkgjs/parseargs": "npm:^0.11.0" + dependenciesMeta: + "@pkgjs/parseargs": + optional: true + checksum: 10c0/6acc10d139eaefdbe04d2f679e6191b3abf073f111edf10b1de5302c97ec93fffeb2fdd8681ed17f16268aa9dd4f8c588ed9d1d3bffbbfa6e8bf897cbb3149b9 + languageName: node + linkType: hard + +"jake@npm:^10.8.5": + version: 10.9.2 + resolution: "jake@npm:10.9.2" + dependencies: + async: "npm:^3.2.3" + chalk: "npm:^4.0.2" + filelist: "npm:^1.0.4" + minimatch: "npm:^3.1.2" + bin: + jake: bin/cli.js + checksum: 10c0/c4597b5ed9b6a908252feab296485a4f87cba9e26d6c20e0ca144fb69e0c40203d34a2efddb33b3d297b8bd59605e6c1f44f6221ca1e10e69175ecbf3ff5fe31 + languageName: node + linkType: hard + +"jest-diff@npm:>=29.4.3 < 30, jest-diff@npm:^29.4.1": + version: 29.7.0 + resolution: "jest-diff@npm:29.7.0" + dependencies: + chalk: "npm:^4.0.0" + diff-sequences: "npm:^29.6.3" + jest-get-type: "npm:^29.6.3" + pretty-format: "npm:^29.7.0" + checksum: 10c0/89a4a7f182590f56f526443dde69acefb1f2f0c9e59253c61d319569856c4931eae66b8a3790c443f529267a0ddba5ba80431c585deed81827032b2b2a1fc999 + languageName: node + linkType: hard + +"jest-get-type@npm:^29.6.3": + version: 29.6.3 + resolution: "jest-get-type@npm:29.6.3" + checksum: 10c0/552e7a97a983d3c2d4e412a44eb7de0430ff773dd99f7500962c268d6dfbfa431d7d08f919c9d960530e5f7f78eb47f267ad9b318265e5092b3ff9ede0db7c2b + languageName: node + linkType: hard + +"jiti@npm:^1.21.0": + version: 1.21.6 + resolution: "jiti@npm:1.21.6" + bin: + jiti: bin/jiti.js + checksum: 10c0/05b9ed58cd30d0c3ccd3c98209339e74f50abd9a17e716f65db46b6a35812103f6bde6e134be7124d01745586bca8cc5dae1d0d952267c3ebe55171949c32e56 + languageName: node + linkType: hard + +"jotai@npm:2.9.0": + version: 2.9.0 + resolution: "jotai@npm:2.9.0" + peerDependencies: + "@types/react": ">=17.0.0" + react: ">=17.0.0" + peerDependenciesMeta: + "@types/react": + optional: true + react: + optional: true + checksum: 10c0/c5551fb90933bcbc28b11cdb4af681398a12f8eb39a4a49568ec6ce5062c2257dd84a85cbfd7ec7d970d56dfa5023d16a0ec7056bc2697fdf9b3ec94da67c9d1 + languageName: node + linkType: hard + +"js-beautify@npm:^1.6.14": + version: 1.15.1 + resolution: "js-beautify@npm:1.15.1" + dependencies: + config-chain: "npm:^1.1.13" + editorconfig: "npm:^1.0.4" + glob: "npm:^10.3.3" + js-cookie: "npm:^3.0.5" + nopt: "npm:^7.2.0" + bin: + css-beautify: js/bin/css-beautify.js + html-beautify: js/bin/html-beautify.js + js-beautify: js/bin/js-beautify.js + checksum: 10c0/4140dd95537143eb429b6c8e47e21310f16c032d97a03163c6c7c0502bc663242a5db08d3ad941b87f24a142ce4f9190c556d2340bcd056545326377dfae5362 + languageName: node + linkType: hard + +"js-cookie@npm:^3.0.5": + version: 3.0.5 + resolution: "js-cookie@npm:3.0.5" + checksum: 10c0/04a0e560407b4489daac3a63e231d35f4e86f78bff9d792011391b49c59f721b513411cd75714c418049c8dc9750b20fcddad1ca5a2ca616c3aca4874cce5b3a + languageName: node + linkType: hard + +"js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": + version: 4.0.0 + resolution: "js-tokens@npm:4.0.0" + checksum: 10c0/e248708d377aa058eacf2037b07ded847790e6de892bbad3dac0abba2e759cb9f121b00099a65195616badcb6eca8d14d975cb3e89eb1cfda644756402c8aeed + languageName: node + linkType: hard + +"js-yaml@npm:4.1.0, js-yaml@npm:^4.1.0": + version: 4.1.0 + resolution: "js-yaml@npm:4.1.0" + dependencies: + argparse: "npm:^2.0.1" + bin: + js-yaml: bin/js-yaml.js + checksum: 10c0/184a24b4eaacfce40ad9074c64fd42ac83cf74d8c8cd137718d456ced75051229e5061b8633c3366b8aada17945a7a356b337828c19da92b51ae62126575018f + languageName: node + linkType: hard + +"js-yaml@npm:^3.10.0, js-yaml@npm:^3.13.1": + version: 3.14.1 + resolution: "js-yaml@npm:3.14.1" + dependencies: + argparse: "npm:^1.0.7" + esprima: "npm:^4.0.0" + bin: + js-yaml: bin/js-yaml.js + checksum: 10c0/6746baaaeac312c4db8e75fa22331d9a04cccb7792d126ed8ce6a0bbcfef0cedaddd0c5098fade53db067c09fe00aa1c957674b4765610a8b06a5a189e46433b + languageName: node + linkType: hard + +"jsbn@npm:1.1.0": + version: 1.1.0 + resolution: "jsbn@npm:1.1.0" + checksum: 10c0/4f907fb78d7b712e11dea8c165fe0921f81a657d3443dde75359ed52eb2b5d33ce6773d97985a089f09a65edd80b11cb75c767b57ba47391fee4c969f7215c96 + languageName: node + linkType: hard + +"jsesc@npm:^2.5.1": + version: 2.5.2 + resolution: "jsesc@npm:2.5.2" + bin: + jsesc: bin/jsesc + checksum: 10c0/dbf59312e0ebf2b4405ef413ec2b25abb5f8f4d9bc5fb8d9f90381622ebca5f2af6a6aa9a8578f65903f9e33990a6dc798edd0ce5586894bf0e9e31803a1de88 + languageName: node + linkType: hard + +"jsesc@npm:~0.5.0": + version: 0.5.0 + resolution: "jsesc@npm:0.5.0" + bin: + jsesc: bin/jsesc + checksum: 10c0/f93792440ae1d80f091b65f8ceddf8e55c4bb7f1a09dee5dcbdb0db5612c55c0f6045625aa6b7e8edb2e0a4feabd80ee48616dbe2d37055573a84db3d24f96d9 + languageName: node + linkType: hard + +"json-parse-better-errors@npm:^1.0.1": + version: 1.0.2 + resolution: "json-parse-better-errors@npm:1.0.2" + checksum: 10c0/2f1287a7c833e397c9ddd361a78638e828fc523038bb3441fd4fc144cfd2c6cd4963ffb9e207e648cf7b692600f1e1e524e965c32df5152120910e4903a47dcb + languageName: node + linkType: hard + +"json-parse-even-better-errors@npm:^2.3.0": + version: 2.3.1 + resolution: "json-parse-even-better-errors@npm:2.3.1" + checksum: 10c0/140932564c8f0b88455432e0f33c4cb4086b8868e37524e07e723f4eaedb9425bdc2bafd71bd1d9765bd15fd1e2d126972bc83990f55c467168c228c24d665f3 + languageName: node + linkType: hard + +"json-parse-even-better-errors@npm:^3.0.0, json-parse-even-better-errors@npm:^3.0.2": + version: 3.0.2 + resolution: "json-parse-even-better-errors@npm:3.0.2" + checksum: 10c0/147f12b005768abe9fab78d2521ce2b7e1381a118413d634a40e6d907d7d10f5e9a05e47141e96d6853af7cc36d2c834d0a014251be48791e037ff2f13d2b94b + languageName: node + linkType: hard + +"json-stringify-nice@npm:^1.1.4": + version: 1.1.4 + resolution: "json-stringify-nice@npm:1.1.4" + checksum: 10c0/13673b67ba9e7fde75a103cade0b0d2dd0d21cd3b918de8d8f6cd59d48ad8c78b0e85f6f4a5842073ddfc91ebdde5ef7c81c7f51945b96a33eaddc5d41324b87 + languageName: node + linkType: hard + +"json-stringify-safe@npm:^5.0.1": + version: 5.0.1 + resolution: "json-stringify-safe@npm:5.0.1" + checksum: 10c0/7dbf35cd0411d1d648dceb6d59ce5857ec939e52e4afc37601aa3da611f0987d5cee5b38d58329ceddf3ed48bd7215229c8d52059ab01f2444a338bf24ed0f37 + languageName: node + linkType: hard + +"json5@npm:^2.2.2, json5@npm:^2.2.3": + version: 2.2.3 + resolution: "json5@npm:2.2.3" + bin: + json5: lib/cli.js + checksum: 10c0/5a04eed94810fa55c5ea138b2f7a5c12b97c3750bc63d11e511dcecbfef758003861522a070c2272764ee0f4e3e323862f386945aeb5b85b87ee43f084ba586c + languageName: node + linkType: hard + +"jsonc-parser@npm:3.2.0": + version: 3.2.0 + resolution: "jsonc-parser@npm:3.2.0" + checksum: 10c0/5a12d4d04dad381852476872a29dcee03a57439574e4181d91dca71904fcdcc5e8e4706c0a68a2c61ad9810e1e1c5806b5100d52d3e727b78f5cdc595401045b + languageName: node + linkType: hard + +"jsonfile@npm:^6.0.1": + version: 6.1.0 + resolution: "jsonfile@npm:6.1.0" + dependencies: + graceful-fs: "npm:^4.1.6" + universalify: "npm:^2.0.0" + dependenciesMeta: + graceful-fs: + optional: true + checksum: 10c0/4f95b5e8a5622b1e9e8f33c96b7ef3158122f595998114d1e7f03985649ea99cb3cd99ce1ed1831ae94c8c8543ab45ebd044207612f31a56fd08462140e46865 + languageName: node + linkType: hard + +"jsonparse@npm:^1.2.0, jsonparse@npm:^1.3.1": + version: 1.3.1 + resolution: "jsonparse@npm:1.3.1" + checksum: 10c0/89bc68080cd0a0e276d4b5ab1b79cacd68f562467008d176dc23e16e97d4efec9e21741d92ba5087a8433526a45a7e6a9d5ef25408696c402ca1cfbc01a90bf0 + languageName: node + linkType: hard + +"jsonwebtoken@npm:^9.0.2": + version: 9.0.2 + resolution: "jsonwebtoken@npm:9.0.2" + dependencies: + jws: "npm:^3.2.2" + lodash.includes: "npm:^4.3.0" + lodash.isboolean: "npm:^3.0.3" + lodash.isinteger: "npm:^4.0.4" + lodash.isnumber: "npm:^3.0.3" + lodash.isplainobject: "npm:^4.0.6" + lodash.isstring: "npm:^4.0.1" + lodash.once: "npm:^4.0.0" + ms: "npm:^2.1.1" + semver: "npm:^7.5.4" + checksum: 10c0/d287a29814895e866db2e5a0209ce730cbc158441a0e5a70d5e940eb0d28ab7498c6bf45029cc8b479639bca94056e9a7f254e2cdb92a2f5750c7f358657a131 + languageName: node + linkType: hard + +"juice@npm:^10.0.0": + version: 10.0.0 + resolution: "juice@npm:10.0.0" + dependencies: + cheerio: "npm:^1.0.0-rc.12" + commander: "npm:^6.1.0" + mensch: "npm:^0.3.4" + slick: "npm:^1.12.2" + web-resource-inliner: "npm:^6.0.1" + bin: + juice: bin/juice + checksum: 10c0/55461554aa564d945460ed9ba6257813cca95c33202449beb37868d14e2dbb46b31d5e4b2eeaefb7e7390d7d7504d0de32668fcbfdcb7e579a732ee954738631 + languageName: node + linkType: hard + +"just-diff-apply@npm:^5.2.0": + version: 5.5.0 + resolution: "just-diff-apply@npm:5.5.0" + checksum: 10c0/d7b85371f2a5a17a108467fda35dddd95264ab438ccec7837b67af5913c57ded7246039d1df2b5bc1ade034ccf815b56d69786c5f1e07383168a066007c796c0 + languageName: node + linkType: hard + +"just-diff@npm:^6.0.0": + version: 6.0.2 + resolution: "just-diff@npm:6.0.2" + checksum: 10c0/1931ca1f0cea4cc480172165c189a84889033ad7a60bee302268ba8ca9f222b43773fd5f272a23ee618d43d85d3048411f06b635571a198159e9a85bb2495f5c + languageName: node + linkType: hard + +"jwa@npm:^1.4.1": + version: 1.4.1 + resolution: "jwa@npm:1.4.1" + dependencies: + buffer-equal-constant-time: "npm:1.0.1" + ecdsa-sig-formatter: "npm:1.0.11" + safe-buffer: "npm:^5.0.1" + checksum: 10c0/5c533540bf38702e73cf14765805a94027c66a0aa8b16bc3e89d8d905e61a4ce2791e87e21be97d1293a5ee9d4f3e5e47737e671768265ca4f25706db551d5e9 + languageName: node + linkType: hard + +"jws@npm:^3.2.2": + version: 3.2.2 + resolution: "jws@npm:3.2.2" + dependencies: + jwa: "npm:^1.4.1" + safe-buffer: "npm:^5.0.1" + checksum: 10c0/e770704533d92df358adad7d1261fdecad4d7b66fa153ba80d047e03ca0f1f73007ce5ed3fbc04d2eba09ba6e7e6e645f351e08e5ab51614df1b0aa4f384dfff + languageName: node + linkType: hard + +"kind-of@npm:^6.0.2, kind-of@npm:^6.0.3": + version: 6.0.3 + resolution: "kind-of@npm:6.0.3" + checksum: 10c0/61cdff9623dabf3568b6445e93e31376bee1cdb93f8ba7033d86022c2a9b1791a1d9510e026e6465ebd701a6dd2f7b0808483ad8838341ac52f003f512e0b4c4 + languageName: node + linkType: hard + +"lerna@npm:^8.1.6": + version: 8.1.6 + resolution: "lerna@npm:8.1.6" + dependencies: + "@lerna/create": "npm:8.1.6" + "@npmcli/arborist": "npm:7.5.3" + "@npmcli/package-json": "npm:5.2.0" + "@npmcli/run-script": "npm:8.1.0" + "@nx/devkit": "npm:>=17.1.2 < 20" + "@octokit/plugin-enterprise-rest": "npm:6.0.1" + "@octokit/rest": "npm:19.0.11" + aproba: "npm:2.0.0" + byte-size: "npm:8.1.1" + chalk: "npm:4.1.0" + clone-deep: "npm:4.0.1" + cmd-shim: "npm:6.0.3" + color-support: "npm:1.1.3" + columnify: "npm:1.6.0" + console-control-strings: "npm:^1.1.0" + conventional-changelog-angular: "npm:7.0.0" + conventional-changelog-core: "npm:5.0.1" + conventional-recommended-bump: "npm:7.0.1" + cosmiconfig: "npm:^8.2.0" + dedent: "npm:1.5.3" + envinfo: "npm:7.13.0" + execa: "npm:5.0.0" + fs-extra: "npm:^11.2.0" + get-port: "npm:5.1.1" + get-stream: "npm:6.0.0" + git-url-parse: "npm:14.0.0" + glob-parent: "npm:6.0.2" + globby: "npm:11.1.0" + graceful-fs: "npm:4.2.11" + has-unicode: "npm:2.0.1" + import-local: "npm:3.1.0" + ini: "npm:^1.3.8" + init-package-json: "npm:6.0.3" + inquirer: "npm:^8.2.4" + is-ci: "npm:3.0.1" + is-stream: "npm:2.0.0" + jest-diff: "npm:>=29.4.3 < 30" + js-yaml: "npm:4.1.0" + libnpmaccess: "npm:8.0.6" + libnpmpublish: "npm:9.0.9" + load-json-file: "npm:6.2.0" + lodash: "npm:^4.17.21" + make-dir: "npm:4.0.0" + minimatch: "npm:3.0.5" + multimatch: "npm:5.0.0" + node-fetch: "npm:2.6.7" + npm-package-arg: "npm:11.0.2" + npm-packlist: "npm:8.0.2" + npm-registry-fetch: "npm:^17.1.0" + nx: "npm:>=17.1.2 < 20" + p-map: "npm:4.0.0" + p-map-series: "npm:2.1.0" + p-pipe: "npm:3.1.0" + p-queue: "npm:6.6.2" + p-reduce: "npm:2.1.0" + p-waterfall: "npm:2.1.1" + pacote: "npm:^18.0.6" + pify: "npm:5.0.0" + read-cmd-shim: "npm:4.0.0" + resolve-from: "npm:5.0.0" + rimraf: "npm:^4.4.1" + semver: "npm:^7.3.8" + set-blocking: "npm:^2.0.0" + signal-exit: "npm:3.0.7" + slash: "npm:3.0.0" + ssri: "npm:^10.0.6" + string-width: "npm:^4.2.3" + strong-log-transformer: "npm:2.1.0" + tar: "npm:6.2.1" + temp-dir: "npm:1.0.0" + typescript: "npm:>=3 < 6" + upath: "npm:2.0.1" + uuid: "npm:^10.0.0" + validate-npm-package-license: "npm:3.0.4" + validate-npm-package-name: "npm:5.0.1" + wide-align: "npm:1.1.5" + write-file-atomic: "npm:5.0.1" + write-pkg: "npm:4.0.0" + yargs: "npm:17.7.2" + yargs-parser: "npm:21.1.1" + bin: + lerna: dist/cli.js + checksum: 10c0/282ec3b5fdc48a2b3ed6eeaac1575e6a35eeaf53c4fa4113ca6b4265d46a422bf5de99b701d7fb4c94e80ec22e0099f24cc5bcab0a4a0a47095538a88e953129 + languageName: node + linkType: hard + +"libnpmaccess@npm:8.0.6": + version: 8.0.6 + resolution: "libnpmaccess@npm:8.0.6" + dependencies: + npm-package-arg: "npm:^11.0.2" + npm-registry-fetch: "npm:^17.0.1" + checksum: 10c0/0b63c7cb44e024b0225dae8ebfe5166a0be8a9420c1b5fb6a4f1c795e9eabbed0fff5984ab57167c5634145de018008cbeeb27fe6f808f611ba5ba1b849ec3d6 + languageName: node + linkType: hard + +"libnpmpublish@npm:9.0.9": + version: 9.0.9 + resolution: "libnpmpublish@npm:9.0.9" + dependencies: + ci-info: "npm:^4.0.0" + normalize-package-data: "npm:^6.0.1" + npm-package-arg: "npm:^11.0.2" + npm-registry-fetch: "npm:^17.0.1" + proc-log: "npm:^4.2.0" + semver: "npm:^7.3.7" + sigstore: "npm:^2.2.0" + ssri: "npm:^10.0.6" + checksum: 10c0/5e4bae455d33fb7402b8b8fcc505d89a1d60ff4b7dc47dd9ba318426c00400e1892fd0435d8db6baab808f64d7f226cbf8d53792244ffad1df7fc2f94f3237fc + languageName: node + linkType: hard + +"lilconfig@npm:^2.1.0": + version: 2.1.0 + resolution: "lilconfig@npm:2.1.0" + checksum: 10c0/64645641aa8d274c99338e130554abd6a0190533c0d9eb2ce7ebfaf2e05c7d9961f3ffe2bfa39efd3b60c521ba3dd24fa236fe2775fc38501bf82bf49d4678b8 + languageName: node + linkType: hard + +"lilconfig@npm:^3.0.0": + version: 3.1.2 + resolution: "lilconfig@npm:3.1.2" + checksum: 10c0/f059630b1a9bddaeba83059db00c672b64dc14074e9f232adce32b38ca1b5686ab737eb665c5ba3c32f147f0002b4bee7311ad0386a9b98547b5623e87071fbe + languageName: node + linkType: hard + +"lines-and-columns@npm:^1.1.6": + version: 1.2.4 + resolution: "lines-and-columns@npm:1.2.4" + checksum: 10c0/3da6ee62d4cd9f03f5dc90b4df2540fb85b352081bee77fe4bbcd12c9000ead7f35e0a38b8d09a9bb99b13223446dd8689ff3c4959807620726d788701a83d2d + languageName: node + linkType: hard + +"lines-and-columns@npm:~2.0.3": + version: 2.0.4 + resolution: "lines-and-columns@npm:2.0.4" + checksum: 10c0/4db28bf065cd7ad897c0700f22d3d0d7c5ed6777e138861c601c496d545340df3fc19e18bd04ff8d95a246a245eb55685b82ca2f8c2ca53a008e9c5316250379 + languageName: node + linkType: hard + +"linkify-it@npm:^5.0.0": + version: 5.0.0 + resolution: "linkify-it@npm:5.0.0" + dependencies: + uc.micro: "npm:^2.0.0" + checksum: 10c0/ff4abbcdfa2003472fc3eb4b8e60905ec97718e11e33cca52059919a4c80cc0e0c2a14d23e23d8c00e5402bc5a885cdba8ca053a11483ab3cc8b3c7a52f88e2d + languageName: node + linkType: hard + +"linkifyjs@npm:^4.1.0": + version: 4.1.3 + resolution: "linkifyjs@npm:4.1.3" + checksum: 10c0/9fb71da06ee710b5587c8b61ff9a0e45303d448f61fab135e44652cff95c09c1abe276158a72384cff6f35a2371d1cec33dfaa7e5280b71dbb142b43d210c75a + languageName: node + linkType: hard + +"load-json-file@npm:6.2.0": + version: 6.2.0 + resolution: "load-json-file@npm:6.2.0" + dependencies: + graceful-fs: "npm:^4.1.15" + parse-json: "npm:^5.0.0" + strip-bom: "npm:^4.0.0" + type-fest: "npm:^0.6.0" + checksum: 10c0/fcb46ef75bab917f37170ba76781a1690bf67144bb53931cb0ed8e4aa20ca439e9c354fcf3594aed531f47dbeb4a49800acab7fdffd553c402ac40c987706d7b + languageName: node + linkType: hard + +"load-json-file@npm:^4.0.0": + version: 4.0.0 + resolution: "load-json-file@npm:4.0.0" + dependencies: + graceful-fs: "npm:^4.1.2" + parse-json: "npm:^4.0.0" + pify: "npm:^3.0.0" + strip-bom: "npm:^3.0.0" + checksum: 10c0/6b48f6a0256bdfcc8970be2c57f68f10acb2ee7e63709b386b2febb6ad3c86198f840889cdbe71d28f741cbaa2f23a7771206b138cd1bdd159564511ca37c1d5 + languageName: node + linkType: hard + +"locate-path@npm:^2.0.0": + version: 2.0.0 + resolution: "locate-path@npm:2.0.0" + dependencies: + p-locate: "npm:^2.0.0" + path-exists: "npm:^3.0.0" + checksum: 10c0/24efa0e589be6aa3c469b502f795126b26ab97afa378846cb508174211515633b770aa0ba610cab113caedab8d2a4902b061a08aaed5297c12ab6f5be4df0133 + languageName: node + linkType: hard + +"locate-path@npm:^5.0.0": + version: 5.0.0 + resolution: "locate-path@npm:5.0.0" + dependencies: + p-locate: "npm:^4.1.0" + checksum: 10c0/33a1c5247e87e022f9713e6213a744557a3e9ec32c5d0b5efb10aa3a38177615bf90221a5592674857039c1a0fd2063b82f285702d37b792d973e9e72ace6c59 + languageName: node + linkType: hard + +"lodash.castarray@npm:^4.4.0": + version: 4.4.0 + resolution: "lodash.castarray@npm:4.4.0" + checksum: 10c0/0bf523ad1596a5bf17869ba047235b4453eee927005013ae152345e2b291b81a02e7f2b7c38f876a1d16f73c34aa3c3241e965193e5b31595035bc8f330c4358 + languageName: node + linkType: hard + +"lodash.debounce@npm:^4.0.8": + version: 4.0.8 + resolution: "lodash.debounce@npm:4.0.8" + checksum: 10c0/762998a63e095412b6099b8290903e0a8ddcb353ac6e2e0f2d7e7d03abd4275fe3c689d88960eb90b0dde4f177554d51a690f22a343932ecbc50a5d111849987 + languageName: node + linkType: hard + +"lodash.defaults@npm:^4.2.0": + version: 4.2.0 + resolution: "lodash.defaults@npm:4.2.0" + checksum: 10c0/d5b77aeb702caa69b17be1358faece33a84497bcca814897383c58b28a2f8dfc381b1d9edbec239f8b425126a3bbe4916223da2a576bb0411c2cefd67df80707 + languageName: node + linkType: hard + +"lodash.includes@npm:^4.3.0": + version: 4.3.0 + resolution: "lodash.includes@npm:4.3.0" + checksum: 10c0/7ca498b9b75bf602d04e48c0adb842dfc7d90f77bcb2a91a2b2be34a723ad24bc1c8b3683ec6b2552a90f216c723cdea530ddb11a3320e08fa38265703978f4b + languageName: node + linkType: hard + +"lodash.isarguments@npm:^3.1.0": + version: 3.1.0 + resolution: "lodash.isarguments@npm:3.1.0" + checksum: 10c0/5e8f95ba10975900a3920fb039a3f89a5a79359a1b5565e4e5b4310ed6ebe64011e31d402e34f577eca983a1fc01ff86c926e3cbe602e1ddfc858fdd353e62d8 + languageName: node + linkType: hard + +"lodash.isboolean@npm:^3.0.3": + version: 3.0.3 + resolution: "lodash.isboolean@npm:3.0.3" + checksum: 10c0/0aac604c1ef7e72f9a6b798e5b676606042401dd58e49f051df3cc1e3adb497b3d7695635a5cbec4ae5f66456b951fdabe7d6b387055f13267cde521f10ec7f7 + languageName: node + linkType: hard + +"lodash.isinteger@npm:^4.0.4": + version: 4.0.4 + resolution: "lodash.isinteger@npm:4.0.4" + checksum: 10c0/4c3e023a2373bf65bf366d3b8605b97ec830bca702a926939bcaa53f8e02789b6a176e7f166b082f9365bfec4121bfeb52e86e9040cb8d450e64c858583f61b7 + languageName: node + linkType: hard + +"lodash.ismatch@npm:^4.4.0": + version: 4.4.0 + resolution: "lodash.ismatch@npm:4.4.0" + checksum: 10c0/8f96a5dc4b8d3fc5a033dcb259d0c3148a1044fa4d02b4a0e8dce0fa1f2ef3ec4ac131e20b5cb2c985a4e9bcb1c37c0aa5af2cef70094959389617347b8fc645 + languageName: node + linkType: hard + +"lodash.isnumber@npm:^3.0.3": + version: 3.0.3 + resolution: "lodash.isnumber@npm:3.0.3" + checksum: 10c0/2d01530513a1ee4f72dd79528444db4e6360588adcb0e2ff663db2b3f642d4bb3d687051ae1115751ca9082db4fdef675160071226ca6bbf5f0c123dbf0aa12d + languageName: node + linkType: hard + +"lodash.isplainobject@npm:^4.0.6": + version: 4.0.6 + resolution: "lodash.isplainobject@npm:4.0.6" + checksum: 10c0/afd70b5c450d1e09f32a737bed06ff85b873ecd3d3d3400458725283e3f2e0bb6bf48e67dbe7a309eb371a822b16a26cca4a63c8c52db3fc7dc9d5f9dd324cbb + languageName: node + linkType: hard + +"lodash.isstring@npm:^4.0.1": + version: 4.0.1 + resolution: "lodash.isstring@npm:4.0.1" + checksum: 10c0/09eaf980a283f9eef58ef95b30ec7fee61df4d6bf4aba3b5f096869cc58f24c9da17900febc8ffd67819b4e29de29793190e88dc96983db92d84c95fa85d1c92 + languageName: node + linkType: hard + +"lodash.merge@npm:^4.6.2": + version: 4.6.2 + resolution: "lodash.merge@npm:4.6.2" + checksum: 10c0/402fa16a1edd7538de5b5903a90228aa48eb5533986ba7fa26606a49db2572bf414ff73a2c9f5d5fd36b31c46a5d5c7e1527749c07cbcf965ccff5fbdf32c506 + languageName: node + linkType: hard + +"lodash.once@npm:^4.0.0": + version: 4.1.1 + resolution: "lodash.once@npm:4.1.1" + checksum: 10c0/46a9a0a66c45dd812fcc016e46605d85ad599fe87d71a02f6736220554b52ffbe82e79a483ad40f52a8a95755b0d1077fba259da8bfb6694a7abbf4a48f1fc04 + languageName: node + linkType: hard + +"lodash@npm:^4.17.15, lodash@npm:^4.17.21": + version: 4.17.21 + resolution: "lodash@npm:4.17.21" + checksum: 10c0/d8cbea072bb08655bb4c989da418994b073a608dffa608b09ac04b43a791b12aeae7cd7ad919aa4c925f33b48490b5cfe6c1f71d827956071dae2e7bb3a6b74c + languageName: node + linkType: hard + +"log-symbols@npm:^4.0.0, log-symbols@npm:^4.1.0": + version: 4.1.0 + resolution: "log-symbols@npm:4.1.0" + dependencies: + chalk: "npm:^4.1.0" + is-unicode-supported: "npm:^0.1.0" + checksum: 10c0/67f445a9ffa76db1989d0fa98586e5bc2fd5247260dafb8ad93d9f0ccd5896d53fb830b0e54dade5ad838b9de2006c826831a3c528913093af20dff8bd24aca6 + languageName: node + linkType: hard + +"loose-envify@npm:^1.1.0, loose-envify@npm:^1.4.0": + version: 1.4.0 + resolution: "loose-envify@npm:1.4.0" + dependencies: + js-tokens: "npm:^3.0.0 || ^4.0.0" + bin: + loose-envify: cli.js + checksum: 10c0/655d110220983c1a4b9c0c679a2e8016d4b67f6e9c7b5435ff5979ecdb20d0813f4dec0a08674fcbdd4846a3f07edbb50a36811fd37930b94aaa0d9daceb017e + languageName: node + linkType: hard + +"lower-case@npm:^1.1.1": + version: 1.1.4 + resolution: "lower-case@npm:1.1.4" + checksum: 10c0/2153ae5490d655a63addc8e7d2f848c6c94803b342ed2d177f75e8073e9fbb50a733d1432c82e1cb8425fa6eae14b2877bf5bbdcb93ab93bb982fb5c3962c57b + languageName: node + linkType: hard + +"lower-case@npm:^2.0.2": + version: 2.0.2 + resolution: "lower-case@npm:2.0.2" + dependencies: + tslib: "npm:^2.0.3" + checksum: 10c0/3d925e090315cf7dc1caa358e0477e186ffa23947740e4314a7429b6e62d72742e0bbe7536a5ae56d19d7618ce998aba05caca53c2902bd5742fdca5fc57fd7b + languageName: node + linkType: hard + +"lowlight@npm:^1.17.0": + version: 1.20.0 + resolution: "lowlight@npm:1.20.0" + dependencies: + fault: "npm:^1.0.0" + highlight.js: "npm:~10.7.0" + checksum: 10c0/728bce6f6fe8b157f48d3324e597f452ce0eed2ccff1c0f41a9047380f944e971eb45bceb31f08fbb64d8f338dabb166f10049b35b92c7ec5cf0241d6adb3dea + languageName: node + linkType: hard + +"lru-cache@npm:^10.0.1, lru-cache@npm:^10.2.0, lru-cache@npm:^10.2.2": + version: 10.4.3 + resolution: "lru-cache@npm:10.4.3" + checksum: 10c0/ebd04fbca961e6c1d6c0af3799adcc966a1babe798f685bb84e6599266599cd95d94630b10262f5424539bc4640107e8a33aa28585374abf561d30d16f4b39fb + languageName: node + linkType: hard + +"lru-cache@npm:^5.1.1": + version: 5.1.1 + resolution: "lru-cache@npm:5.1.1" + dependencies: + yallist: "npm:^3.0.2" + checksum: 10c0/89b2ef2ef45f543011e38737b8a8622a2f8998cddf0e5437174ef8f1f70a8b9d14a918ab3e232cb3ba343b7abddffa667f0b59075b2b80e6b4d63c3de6127482 + languageName: node + linkType: hard + +"lru-cache@npm:^6.0.0": + version: 6.0.0 + resolution: "lru-cache@npm:6.0.0" + dependencies: + yallist: "npm:^4.0.0" + checksum: 10c0/cb53e582785c48187d7a188d3379c181b5ca2a9c78d2bce3e7dee36f32761d1c42983da3fe12b55cb74e1779fa94cdc2e5367c028a9b35317184ede0c07a30a9 + languageName: node + linkType: hard + +"lucide-react@npm:^0.408.0": + version: 0.408.0 + resolution: "lucide-react@npm:0.408.0" + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + checksum: 10c0/b79f17a4748c1cd17aa260dc5d226b64c50304b187b922f09ac4d4cb02304b2837f553eb8e75d151cde3957ebe1b3f7b120a324104835c1bcf82e5b401dedd6f + languageName: node + linkType: hard + +"make-dir@npm:4.0.0": + version: 4.0.0 + resolution: "make-dir@npm:4.0.0" + dependencies: + semver: "npm:^7.5.3" + checksum: 10c0/69b98a6c0b8e5c4fe9acb61608a9fbcfca1756d910f51e5dbe7a9e5cfb74fca9b8a0c8a0ffdf1294a740826c1ab4871d5bf3f62f72a3049e5eac6541ddffed68 + languageName: node + linkType: hard + +"make-dir@npm:^2.1.0": + version: 2.1.0 + resolution: "make-dir@npm:2.1.0" + dependencies: + pify: "npm:^4.0.1" + semver: "npm:^5.6.0" + checksum: 10c0/ada869944d866229819735bee5548944caef560d7a8536ecbc6536edca28c72add47cc4f6fc39c54fb25d06b58da1f8994cf7d9df7dadea047064749efc085d8 + languageName: node + linkType: hard + +"make-dir@npm:^3.1.0": + version: 3.1.0 + resolution: "make-dir@npm:3.1.0" + dependencies: + semver: "npm:^6.0.0" + checksum: 10c0/56aaafefc49c2dfef02c5c95f9b196c4eb6988040cf2c712185c7fe5c99b4091591a7fc4d4eafaaefa70ff763a26f6ab8c3ff60b9e75ea19876f49b18667ecaa + languageName: node + linkType: hard + +"make-error@npm:^1.1.1": + version: 1.3.6 + resolution: "make-error@npm:1.3.6" + checksum: 10c0/171e458d86854c6b3fc46610cfacf0b45149ba043782558c6875d9f42f222124384ad0b468c92e996d815a8a2003817a710c0a160e49c1c394626f76fa45396f + languageName: node + linkType: hard + +"make-fetch-happen@npm:^13.0.0, make-fetch-happen@npm:^13.0.1": + version: 13.0.1 + resolution: "make-fetch-happen@npm:13.0.1" + dependencies: + "@npmcli/agent": "npm:^2.0.0" + cacache: "npm:^18.0.0" + http-cache-semantics: "npm:^4.1.1" + is-lambda: "npm:^1.0.1" + minipass: "npm:^7.0.2" + minipass-fetch: "npm:^3.0.0" + minipass-flush: "npm:^1.0.5" + minipass-pipeline: "npm:^1.2.4" + negotiator: "npm:^0.6.3" + proc-log: "npm:^4.2.0" + promise-retry: "npm:^2.0.1" + ssri: "npm:^10.0.0" + checksum: 10c0/df5f4dbb6d98153b751bccf4dc4cc500de85a96a9331db9805596c46aa9f99d9555983954e6c1266d9f981ae37a9e4647f42b9a4bb5466f867f4012e582c9e7e + languageName: node + linkType: hard + +"map-obj@npm:^1.0.0": + version: 1.0.1 + resolution: "map-obj@npm:1.0.1" + checksum: 10c0/ccca88395e7d38671ed9f5652ecf471ecd546924be2fb900836b9da35e068a96687d96a5f93dcdfa94d9a27d649d2f10a84595590f89a347fb4dda47629dcc52 + languageName: node + linkType: hard + +"map-obj@npm:^4.0.0": + version: 4.3.0 + resolution: "map-obj@npm:4.3.0" + checksum: 10c0/1c19e1c88513c8abdab25c316367154c6a0a6a0f77e3e8c391bb7c0e093aefed293f539d026dc013d86219e5e4c25f23b0003ea588be2101ccd757bacc12d43b + languageName: node + linkType: hard + +"markdown-it@npm:^14.0.0": + version: 14.1.0 + resolution: "markdown-it@npm:14.1.0" + dependencies: + argparse: "npm:^2.0.1" + entities: "npm:^4.4.0" + linkify-it: "npm:^5.0.0" + mdurl: "npm:^2.0.0" + punycode.js: "npm:^2.3.1" + uc.micro: "npm:^2.1.0" + bin: + markdown-it: bin/markdown-it.mjs + checksum: 10c0/9a6bb444181d2db7016a4173ae56a95a62c84d4cbfb6916a399b11d3e6581bf1cc2e4e1d07a2f022ae72c25f56db90fbe1e529fca16fbf9541659dc53480d4b4 + languageName: node + linkType: hard + +"mdn-data@npm:2.0.28": + version: 2.0.28 + resolution: "mdn-data@npm:2.0.28" + checksum: 10c0/20000932bc4cd1cde9cba4e23f08cc4f816398af4c15ec81040ed25421d6bf07b5cf6b17095972577fb498988f40f4cb589e3169b9357bb436a12d8e07e5ea7b + languageName: node + linkType: hard + +"mdn-data@npm:2.0.30": + version: 2.0.30 + resolution: "mdn-data@npm:2.0.30" + checksum: 10c0/a2c472ea16cee3911ae742593715aa4c634eb3d4b9f1e6ada0902aa90df13dcbb7285d19435f3ff213ebaa3b2e0c0265c1eb0e3fb278fda7f8919f046a410cd9 + languageName: node + linkType: hard + +"mdurl@npm:^2.0.0": + version: 2.0.0 + resolution: "mdurl@npm:2.0.0" + checksum: 10c0/633db522272f75ce4788440669137c77540d74a83e9015666a9557a152c02e245b192edc20bc90ae953bbab727503994a53b236b4d9c99bdaee594d0e7dd2ce0 + languageName: node + linkType: hard + +"media-typer@npm:0.3.0": + version: 0.3.0 + resolution: "media-typer@npm:0.3.0" + checksum: 10c0/d160f31246907e79fed398470285f21bafb45a62869dc469b1c8877f3f064f5eabc4bcc122f9479b8b605bc5c76187d7871cf84c4ee3ecd3e487da1993279928 + languageName: node + linkType: hard + +"mensch@npm:^0.3.4": + version: 0.3.4 + resolution: "mensch@npm:0.3.4" + checksum: 10c0/177f9c1cb1acd93da98a971288a5da99f819ac06de19ca450040b18ddf8728c7ae0ce22309fadbbfd4ceb773bc5c03bf1cb93ceb91441da9e76e010d314da2ea + languageName: node + linkType: hard + +"meow@npm:^8.1.2": + version: 8.1.2 + resolution: "meow@npm:8.1.2" + dependencies: + "@types/minimist": "npm:^1.2.0" + camelcase-keys: "npm:^6.2.2" + decamelize-keys: "npm:^1.1.0" + hard-rejection: "npm:^2.1.0" + minimist-options: "npm:4.1.0" + normalize-package-data: "npm:^3.0.0" + read-pkg-up: "npm:^7.0.1" + redent: "npm:^3.0.0" + trim-newlines: "npm:^3.0.0" + type-fest: "npm:^0.18.0" + yargs-parser: "npm:^20.2.3" + checksum: 10c0/9a8d90e616f783650728a90f4ea1e5f763c1c5260369e6596b52430f877f4af8ecbaa8c9d952c93bbefd6d5bda4caed6a96a20ba7d27b511d2971909b01922a2 + languageName: node + linkType: hard + +"merge-descriptors@npm:1.0.1": + version: 1.0.1 + resolution: "merge-descriptors@npm:1.0.1" + checksum: 10c0/b67d07bd44cfc45cebdec349bb6e1f7b077ee2fd5beb15d1f7af073849208cb6f144fe403e29a36571baf3f4e86469ac39acf13c318381e958e186b2766f54ec + languageName: node + linkType: hard + +"merge-stream@npm:^2.0.0": + version: 2.0.0 + resolution: "merge-stream@npm:2.0.0" + checksum: 10c0/867fdbb30a6d58b011449b8885601ec1690c3e41c759ecd5a9d609094f7aed0096c37823ff4a7190ef0b8f22cc86beb7049196ff68c016e3b3c671d0dac91ce5 + languageName: node + linkType: hard + +"merge2@npm:^1.3.0, merge2@npm:^1.4.1": + version: 1.4.1 + resolution: "merge2@npm:1.4.1" + checksum: 10c0/254a8a4605b58f450308fc474c82ac9a094848081bf4c06778200207820e5193726dc563a0d2c16468810516a5c97d9d3ea0ca6585d23c58ccfff2403e8dbbeb + languageName: node + linkType: hard + +"methods@npm:~1.1.2": + version: 1.1.2 + resolution: "methods@npm:1.1.2" + checksum: 10c0/bdf7cc72ff0a33e3eede03708c08983c4d7a173f91348b4b1e4f47d4cdbf734433ad971e7d1e8c77247d9e5cd8adb81ea4c67b0a2db526b758b2233d7814b8b2 + languageName: node + linkType: hard + +"micromatch@npm:^4.0.4, micromatch@npm:^4.0.5": + version: 4.0.7 + resolution: "micromatch@npm:4.0.7" + dependencies: + braces: "npm:^3.0.3" + picomatch: "npm:^2.3.1" + checksum: 10c0/58fa99bc5265edec206e9163a1d2cec5fabc46a5b473c45f4a700adce88c2520456ae35f2b301e4410fb3afb27e9521fb2813f6fc96be0a48a89430e0916a772 + languageName: node + linkType: hard + +"mime-db@npm:1.52.0": + version: 1.52.0 + resolution: "mime-db@npm:1.52.0" + checksum: 10c0/0557a01deebf45ac5f5777fe7740b2a5c309c6d62d40ceab4e23da9f821899ce7a900b7ac8157d4548ddbb7beffe9abc621250e6d182b0397ec7f10c7b91a5aa + languageName: node + linkType: hard + +"mime-db@npm:>= 1.43.0 < 2": + version: 1.53.0 + resolution: "mime-db@npm:1.53.0" + checksum: 10c0/1dcc37ba8ed5d1c179f5c6f0837e8db19371d5f2ea3690c3c2f3fa8c3858f976851d3460b172b4dee78ebd606762cbb407aa398545fbacd539e519f858cd7bf4 + languageName: node + linkType: hard + +"mime-types@npm:^2.1.12, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": + version: 2.1.35 + resolution: "mime-types@npm:2.1.35" + dependencies: + mime-db: "npm:1.52.0" + checksum: 10c0/82fb07ec56d8ff1fc999a84f2f217aa46cb6ed1033fefaabd5785b9a974ed225c90dc72fff460259e66b95b73648596dbcc50d51ed69cdf464af2d237d3149b2 + languageName: node + linkType: hard + +"mime@npm:1.6.0": + version: 1.6.0 + resolution: "mime@npm:1.6.0" + bin: + mime: cli.js + checksum: 10c0/b92cd0adc44888c7135a185bfd0dddc42c32606401c72896a842ae15da71eb88858f17669af41e498b463cd7eb998f7b48939a25b08374c7924a9c8a6f8a81b0 + languageName: node + linkType: hard + +"mime@npm:^2.4.6": + version: 2.6.0 + resolution: "mime@npm:2.6.0" + bin: + mime: cli.js + checksum: 10c0/a7f2589900d9c16e3bdf7672d16a6274df903da958c1643c9c45771f0478f3846dcb1097f31eb9178452570271361e2149310931ec705c037210fc69639c8e6c + languageName: node + linkType: hard + +"mimic-fn@npm:^2.1.0": + version: 2.1.0 + resolution: "mimic-fn@npm:2.1.0" + checksum: 10c0/b26f5479d7ec6cc2bce275a08f146cf78f5e7b661b18114e2506dd91ec7ec47e7a25bf4360e5438094db0560bcc868079fb3b1fb3892b833c1ecbf63f80c95a4 + languageName: node + linkType: hard + +"min-indent@npm:^1.0.0": + version: 1.0.1 + resolution: "min-indent@npm:1.0.1" + checksum: 10c0/7e207bd5c20401b292de291f02913230cb1163abca162044f7db1d951fa245b174dc00869d40dd9a9f32a885ad6a5f3e767ee104cf278f399cb4e92d3f582d5c + languageName: node + linkType: hard + +"mini-svg-data-uri@npm:^1.2.3": + version: 1.4.4 + resolution: "mini-svg-data-uri@npm:1.4.4" + bin: + mini-svg-data-uri: cli.js + checksum: 10c0/24545fa30b5a45449241bf19c25b8bc37594b63ec06401b3d563bd1c2e8a6abb7c18741f8b354e0064baa63c291be214154bf3a66f201ae71dfab3cc1a5e3191 + languageName: node + linkType: hard + +"minimatch@npm:3.0.5": + version: 3.0.5 + resolution: "minimatch@npm:3.0.5" + dependencies: + brace-expansion: "npm:^1.1.7" + checksum: 10c0/f398652d0d260137c289c270a4ac98ebe0a27cd316fa0fac72b096e96cbdc89f71d80d47ac7065c716ba3b0b730783b19180bd85a35f9247535d2adfe96bba76 + languageName: node + linkType: hard + +"minimatch@npm:9.0.1": + version: 9.0.1 + resolution: "minimatch@npm:9.0.1" + dependencies: + brace-expansion: "npm:^2.0.1" + checksum: 10c0/aa043eb8822210b39888a5d0d28df0017b365af5add9bd522f180d2a6962de1cbbf1bdeacdb1b17f410dc3336bc8d76fb1d3e814cdc65d00c2f68e01f0010096 + languageName: node + linkType: hard + +"minimatch@npm:9.0.3": + version: 9.0.3 + resolution: "minimatch@npm:9.0.3" + dependencies: + brace-expansion: "npm:^2.0.1" + checksum: 10c0/85f407dcd38ac3e180f425e86553911d101455ca3ad5544d6a7cec16286657e4f8a9aa6695803025c55e31e35a91a2252b5dc8e7d527211278b8b65b4dbd5eac + languageName: node + linkType: hard + +"minimatch@npm:^3.0.4, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": + version: 3.1.2 + resolution: "minimatch@npm:3.1.2" + dependencies: + brace-expansion: "npm:^1.1.7" + checksum: 10c0/0262810a8fc2e72cca45d6fd86bd349eee435eb95ac6aa45c9ea2180e7ee875ef44c32b55b5973ceabe95ea12682f6e3725cbb63d7a2d1da3ae1163c8b210311 + languageName: node + linkType: hard + +"minimatch@npm:^5.0.1": + version: 5.1.6 + resolution: "minimatch@npm:5.1.6" + dependencies: + brace-expansion: "npm:^2.0.1" + checksum: 10c0/3defdfd230914f22a8da203747c42ee3c405c39d4d37ffda284dac5e45b7e1f6c49aa8be606509002898e73091ff2a3bbfc59c2c6c71d4660609f63aa92f98e3 + languageName: node + linkType: hard + +"minimatch@npm:^8.0.2": + version: 8.0.4 + resolution: "minimatch@npm:8.0.4" + dependencies: + brace-expansion: "npm:^2.0.1" + checksum: 10c0/a0a394c356dd5b4cb7f821720841a82fa6f07c9c562c5b716909d1b6ec5e56a7e4c4b5029da26dd256b7d2b3a3f38cbf9ddd8680e887b9b5282b09c05501c1ca + languageName: node + linkType: hard + +"minimatch@npm:^9.0.0, minimatch@npm:^9.0.3, minimatch@npm:^9.0.4": + version: 9.0.5 + resolution: "minimatch@npm:9.0.5" + dependencies: + brace-expansion: "npm:^2.0.1" + checksum: 10c0/de96cf5e35bdf0eab3e2c853522f98ffbe9a36c37797778d2665231ec1f20a9447a7e567cb640901f89e4daaa95ae5d70c65a9e8aa2bb0019b6facbc3c0575ed + languageName: node + linkType: hard + +"minimist-options@npm:4.1.0": + version: 4.1.0 + resolution: "minimist-options@npm:4.1.0" + dependencies: + arrify: "npm:^1.0.1" + is-plain-obj: "npm:^1.1.0" + kind-of: "npm:^6.0.3" + checksum: 10c0/7871f9cdd15d1e7374e5b013e2ceda3d327a06a8c7b38ae16d9ef941e07d985e952c589e57213f7aa90a8744c60aed9524c0d85e501f5478382d9181f2763f54 + languageName: node + linkType: hard + +"minimist@npm:^1.2.0, minimist@npm:^1.2.5, minimist@npm:^1.2.6": + version: 1.2.8 + resolution: "minimist@npm:1.2.8" + checksum: 10c0/19d3fcdca050087b84c2029841a093691a91259a47def2f18222f41e7645a0b7c44ef4b40e88a1e58a40c84d2ef0ee6047c55594d298146d0eb3f6b737c20ce6 + languageName: node + linkType: hard + +"minipass-collect@npm:^2.0.1": + version: 2.0.1 + resolution: "minipass-collect@npm:2.0.1" + dependencies: + minipass: "npm:^7.0.3" + checksum: 10c0/5167e73f62bb74cc5019594709c77e6a742051a647fe9499abf03c71dca75515b7959d67a764bdc4f8b361cf897fbf25e2d9869ee039203ed45240f48b9aa06e + languageName: node + linkType: hard + +"minipass-fetch@npm:^3.0.0": + version: 3.0.5 + resolution: "minipass-fetch@npm:3.0.5" + dependencies: + encoding: "npm:^0.1.13" + minipass: "npm:^7.0.3" + minipass-sized: "npm:^1.0.3" + minizlib: "npm:^2.1.2" + dependenciesMeta: + encoding: + optional: true + checksum: 10c0/9d702d57f556274286fdd97e406fc38a2f5c8d15e158b498d7393b1105974b21249289ec571fa2b51e038a4872bfc82710111cf75fae98c662f3d6f95e72152b + languageName: node + linkType: hard + +"minipass-flush@npm:^1.0.5": + version: 1.0.5 + resolution: "minipass-flush@npm:1.0.5" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10c0/2a51b63feb799d2bb34669205eee7c0eaf9dce01883261a5b77410c9408aa447e478efd191b4de6fc1101e796ff5892f8443ef20d9544385819093dbb32d36bd + languageName: node + linkType: hard + +"minipass-pipeline@npm:^1.2.4": + version: 1.2.4 + resolution: "minipass-pipeline@npm:1.2.4" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10c0/cbda57cea20b140b797505dc2cac71581a70b3247b84480c1fed5ca5ba46c25ecc25f68bfc9e6dcb1a6e9017dab5c7ada5eab73ad4f0a49d84e35093e0c643f2 + languageName: node + linkType: hard + +"minipass-sized@npm:^1.0.3": + version: 1.0.3 + resolution: "minipass-sized@npm:1.0.3" + dependencies: + minipass: "npm:^3.0.0" + checksum: 10c0/298f124753efdc745cfe0f2bdfdd81ba25b9f4e753ca4a2066eb17c821f25d48acea607dfc997633ee5bf7b6dfffb4eee4f2051eb168663f0b99fad2fa4829cb + languageName: node + linkType: hard + +"minipass@npm:^3.0.0": + version: 3.3.6 + resolution: "minipass@npm:3.3.6" + dependencies: + yallist: "npm:^4.0.0" + checksum: 10c0/a114746943afa1dbbca8249e706d1d38b85ed1298b530f5808ce51f8e9e941962e2a5ad2e00eae7dd21d8a4aae6586a66d4216d1a259385e9d0358f0c1eba16c + languageName: node + linkType: hard + +"minipass@npm:^4.2.4": + version: 4.2.8 + resolution: "minipass@npm:4.2.8" + checksum: 10c0/4ea76b030d97079f4429d6e8a8affd90baf1b6a1898977c8ccce4701c5a2ba2792e033abc6709373f25c2c4d4d95440d9d5e9464b46b7b76ca44d2ce26d939ce + languageName: node + linkType: hard + +"minipass@npm:^5.0.0": + version: 5.0.0 + resolution: "minipass@npm:5.0.0" + checksum: 10c0/a91d8043f691796a8ac88df039da19933ef0f633e3d7f0d35dcd5373af49131cf2399bfc355f41515dc495e3990369c3858cd319e5c2722b4753c90bf3152462 + languageName: node + linkType: hard + +"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3, minipass@npm:^7.1.2": + version: 7.1.2 + resolution: "minipass@npm:7.1.2" + checksum: 10c0/b0fd20bb9fb56e5fa9a8bfac539e8915ae07430a619e4b86ff71f5fc757ef3924b23b2c4230393af1eda647ed3d75739e4e0acb250a6b1eb277cf7f8fe449557 + languageName: node + linkType: hard + +"minizlib@npm:^2.1.1, minizlib@npm:^2.1.2": + version: 2.1.2 + resolution: "minizlib@npm:2.1.2" + dependencies: + minipass: "npm:^3.0.0" + yallist: "npm:^4.0.0" + checksum: 10c0/64fae024e1a7d0346a1102bb670085b17b7f95bf6cfdf5b128772ec8faf9ea211464ea4add406a3a6384a7d87a0cd1a96263692134323477b4fb43659a6cab78 + languageName: node + linkType: hard + +"mjml-accordion@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-accordion@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.15.3" + checksum: 10c0/d03bfbb6cf904f8c3a2aeef5c722e8d29aeb69c57dfc2f569ac90ca374a6c896276057dea5ec4742bf537d46cf789f5d41eb98baeaa54614946dcab0cd24bbff + languageName: node + linkType: hard + +"mjml-body@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-body@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.15.3" + checksum: 10c0/59e177506bd08fd65865ac8c17bf8b96ae5a1e841e86b2967371162b06442f300afdac24031ec847dca0313dc0ba33b63c20886b1f0f26aa8a4715f0db2ddeda + languageName: node + linkType: hard + +"mjml-button@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-button@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.15.3" + checksum: 10c0/6619ab6a407d117863a7683591f27082a2131e107b8f849e77ecd3db89a177ed1af4a83ed96b08e84564cc9414b91c909f2b1c775d80bce529f284db910032d6 + languageName: node + linkType: hard + +"mjml-carousel@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-carousel@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.15.3" + checksum: 10c0/29a5fad7814792e1ccda464f7d77350cc0817ab941170fbb9aecbfcc4f85cf9daefb697fed93d7621f930b4e73f5a8ce69158f6e8434aff018c1f69bd13ff80b + languageName: node + linkType: hard + +"mjml-cli@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-cli@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + chokidar: "npm:^3.0.0" + glob: "npm:^10.3.10" + html-minifier: "npm:^4.0.0" + js-beautify: "npm:^1.6.14" + lodash: "npm:^4.17.21" + minimatch: "npm:^9.0.3" + mjml-core: "npm:4.15.3" + mjml-migrate: "npm:4.15.3" + mjml-parser-xml: "npm:4.15.3" + mjml-validator: "npm:4.15.3" + yargs: "npm:^17.7.2" + bin: + mjml-cli: bin/mjml + checksum: 10c0/82aa3fe91a4457a887ac00d86830681e88fda65365422ef0370bd03a327e4b2baa75dc6d822534cbfc91f9e4b820229de43410a9c432627fc439ddd8a9c3caf8 + languageName: node + linkType: hard + +"mjml-column@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-column@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.15.3" + checksum: 10c0/55f6f179d5f18de873d59579e62f2b3dfa67325a7d8d7ac77f9ed4c9451b3c70af68d36c0256f3244c4cc567aff77373f6c54b67819b8de9d08673f69f113952 + languageName: node + linkType: hard + +"mjml-core@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-core@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + cheerio: "npm:1.0.0-rc.12" + detect-node: "npm:^2.0.4" + html-minifier: "npm:^4.0.0" + js-beautify: "npm:^1.6.14" + juice: "npm:^10.0.0" + lodash: "npm:^4.17.21" + mjml-migrate: "npm:4.15.3" + mjml-parser-xml: "npm:4.15.3" + mjml-validator: "npm:4.15.3" + checksum: 10c0/75612453055a3e07e4da40e3f88db7925170f5a210fdaa8ec798eacde2b825862d9d6228ab3b45158e0a9a063720fdc3e83f9a2fd2c0978eaa2323a668bed2f0 + languageName: node + linkType: hard + +"mjml-divider@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-divider@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.15.3" + checksum: 10c0/122c2c3af5736ae163624fd5342781d3611c04768ddf33a5f755a8f76b21df000a911b1f44c5b325f5eb05bd9d726cdc72852e304ff939bfca701430a97466ff + languageName: node + linkType: hard + +"mjml-group@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-group@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.15.3" + checksum: 10c0/61ac4504c75d07a2430172d4e057c933ff1d428315c2acf73f773961e02795a042e058487040aacb0da4822f1031c4b889e08ffba574098c9b3a166184b02f0c + languageName: node + linkType: hard + +"mjml-head-attributes@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-head-attributes@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.15.3" + checksum: 10c0/733000b0d6e823b2ea91518e795f271f35fd410698ee4cb02db29a22b267d8f0daf9fb5562cd4d431421a4b8e70371ba962ccb41469f5ed8a31be4deca60905a + languageName: node + linkType: hard + +"mjml-head-breakpoint@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-head-breakpoint@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.15.3" + checksum: 10c0/23930e576a6e4f7b7add412d255bafac917612d9a317b5086893a4b3688fa78d99112748067579d76308a545975a8deec7dd32013b326c08a4864ff7c4e86917 + languageName: node + linkType: hard + +"mjml-head-font@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-head-font@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.15.3" + checksum: 10c0/4ee959a3742694e21de42b156c2e245c4ade84977dc95b82ca232882129672f66dac59bdbb3398b9471c9583d9876088be59be6261beb86ac23941a21877d413 + languageName: node + linkType: hard + +"mjml-head-html-attributes@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-head-html-attributes@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.15.3" + checksum: 10c0/3161edf6cff97f47be0780ddcdba8d4c9c195c0a42d824c191c234fa3944b5f7ec61d762f9e59ecf307e53040d1d667fb6ba5729f308d85aff082a14e48ce2b6 + languageName: node + linkType: hard + +"mjml-head-preview@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-head-preview@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.15.3" + checksum: 10c0/9d53c0615e691699f94d5686376ca7342fb403f8511b9031b482a8b60e4b54ddda9a924153149b8ea650411db54e9a84c4292e66e3d5b097ad3725fbbd567a41 + languageName: node + linkType: hard + +"mjml-head-style@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-head-style@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.15.3" + checksum: 10c0/b7bc2ec2065c5758016a792fbb3fd5c31beaa48257581a36a3e710225d4b6c6efe807c23f2f69e883d71a670e696fd9ea17654805603c7f67ee2556e28f96ba6 + languageName: node + linkType: hard + +"mjml-head-title@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-head-title@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.15.3" + checksum: 10c0/2f0d9b0326acfb9fdd7e0c4270e8359dc661a4c250c07b0966da82f0998030f8e4cdd95c9539a4ca41264bd7ee0c11cd0cb98bbe19d3bad9af38afb92fb16582 + languageName: node + linkType: hard + +"mjml-head@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-head@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.15.3" + checksum: 10c0/db09298416864d8ddfc45ea7c18e2117d1786668aec4b8b8dfa14e36ef48c9f0c4d1d097fb6c3b769b76f5a105ce641ce9f3db5e82971e1bd412bc100f24c824 + languageName: node + linkType: hard + +"mjml-hero@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-hero@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.15.3" + checksum: 10c0/d7960de47f0fc23099a94d201c27285729765e7072e4eb8ea456141d8dcb05be52e39b01c27ba23ca82a43e35a0c5cbc3b042dcaf724bc7c4b21e62d450aa4e2 + languageName: node + linkType: hard + +"mjml-image@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-image@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.15.3" + checksum: 10c0/2f4442dc03196c0697aad3fcfd42d5991ef902587d37a416169d7c6c605df7044d8081a4beec673689b7ab20f95d31c6d1834948c2229d84283eee138ccf655a + languageName: node + linkType: hard + +"mjml-migrate@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-migrate@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + js-beautify: "npm:^1.6.14" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.15.3" + mjml-parser-xml: "npm:4.15.3" + yargs: "npm:^17.7.2" + bin: + migrate: lib/cli.js + checksum: 10c0/6e5a82426d19d466372325b7aad4170744992ee69d8bf5bd437cea20bec30803458456a933592f6f1a5af9ed3aa6a6f5d4e8b44150c21ca268fcd7a8f7f9f51e + languageName: node + linkType: hard + +"mjml-navbar@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-navbar@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.15.3" + checksum: 10c0/abfa04c5b6d734e645ff24f7f557e5bab44d2b7ccc1f0ce8854c5121efe449118475ef525f940ccfc5b3dad1de9c71f1e15d2fbf9fb896a84e1a26a2bfbb0e23 + languageName: node + linkType: hard + +"mjml-parser-xml@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-parser-xml@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + detect-node: "npm:2.1.0" + htmlparser2: "npm:^9.1.0" + lodash: "npm:^4.17.15" + checksum: 10c0/d88a0552360589c7e82a0772d734c0d8975ad91ee5764756fc3e3cc28d062fecc87ca9b9b763f3a0309e96d1e1b66c0e707aa4bc8d7065f8121b256aa8efc85d + languageName: node + linkType: hard + +"mjml-preset-core@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-preset-core@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + mjml-accordion: "npm:4.15.3" + mjml-body: "npm:4.15.3" + mjml-button: "npm:4.15.3" + mjml-carousel: "npm:4.15.3" + mjml-column: "npm:4.15.3" + mjml-divider: "npm:4.15.3" + mjml-group: "npm:4.15.3" + mjml-head: "npm:4.15.3" + mjml-head-attributes: "npm:4.15.3" + mjml-head-breakpoint: "npm:4.15.3" + mjml-head-font: "npm:4.15.3" + mjml-head-html-attributes: "npm:4.15.3" + mjml-head-preview: "npm:4.15.3" + mjml-head-style: "npm:4.15.3" + mjml-head-title: "npm:4.15.3" + mjml-hero: "npm:4.15.3" + mjml-image: "npm:4.15.3" + mjml-navbar: "npm:4.15.3" + mjml-raw: "npm:4.15.3" + mjml-section: "npm:4.15.3" + mjml-social: "npm:4.15.3" + mjml-spacer: "npm:4.15.3" + mjml-table: "npm:4.15.3" + mjml-text: "npm:4.15.3" + mjml-wrapper: "npm:4.15.3" + checksum: 10c0/07112b5e0a72a1c71b65f5a0936d61e06b11b5d6e0ec7a68351c1bef37b0ba2b95e90bed9ecdf3e823f44c9ad80ceec3d2b217078463bf97655b9de604d398e4 + languageName: node + linkType: hard + +"mjml-raw@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-raw@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.15.3" + checksum: 10c0/ad6f42d293bc428b24eb1bea3f749a92e6de10a9b6fef927166ce3b7c7eb0b651c1f9a10a105dc1ae9c7247c7cb50091d64bb0a35d851620c7589596dde764fb + languageName: node + linkType: hard + +"mjml-section@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-section@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.15.3" + checksum: 10c0/395dae3f4bd6201a5a1ac66af4cea7c7a988017e122ce661853935b3312e3c1722d35028b889024b0abe78c086c64d1ad387f683f9a9c44fd2b1e3a528f6d881 + languageName: node + linkType: hard + +"mjml-social@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-social@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.15.3" + checksum: 10c0/2c7e24fbd81498690c3dab2cea9f2ac1858a20caad3fa00c11fa833fbccb57eaf01b1cfaadf12b9a279b620b46ce1027d5b0291f9b61ccb8bfe5c0ac9847722d + languageName: node + linkType: hard + +"mjml-spacer@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-spacer@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.15.3" + checksum: 10c0/0956418975f12bd0002393e52d4468bf44aff4df57c50711f77be6dbc3856669278366f1f8d264163950793670246fb0f64f1544ba67249c9a74e397b0df06f9 + languageName: node + linkType: hard + +"mjml-table@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-table@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.15.3" + checksum: 10c0/a4ea3c476ce1c1c15ce42092e8a8e7db3044ab3ddaf0542f62c00c483d087ebb6cc42ef7d1dd777e052378becc4591395f649752ecb4769b08f0740222a217f7 + languageName: node + linkType: hard + +"mjml-text@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-text@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.15.3" + checksum: 10c0/e652ad95269d262a3d67d3b00eb28a356d4a830db77858ce31705ff980ba3f63a5775992b9c8189225056db6d3157e7dda994cbd771a2eef9ad7913a01ea0ef7 + languageName: node + linkType: hard + +"mjml-validator@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-validator@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + checksum: 10c0/d9321b483e7e1fca1e0a64d44d24a6d8c9e55c1f01966500a6f8f0e8ee8c0755930d9745c722d4ebcd5daf783fcbf52cd3cc04fb8f2ec46c1799a7af55866ea1 + languageName: node + linkType: hard + +"mjml-wrapper@npm:4.15.3": + version: 4.15.3 + resolution: "mjml-wrapper@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + lodash: "npm:^4.17.21" + mjml-core: "npm:4.15.3" + mjml-section: "npm:4.15.3" + checksum: 10c0/d3b4bbcb16bcacd5c2b7cbb0291c4a947fa0d14b5ba4fca82dc12719746e9bedf385c4030a204c79eca230c1c740b62ead4d5dd30e691c8d10fb31c0af070981 + languageName: node + linkType: hard + +"mjml@npm:^4.15.3": + version: 4.15.3 + resolution: "mjml@npm:4.15.3" + dependencies: + "@babel/runtime": "npm:^7.23.9" + mjml-cli: "npm:4.15.3" + mjml-core: "npm:4.15.3" + mjml-migrate: "npm:4.15.3" + mjml-preset-core: "npm:4.15.3" + mjml-validator: "npm:4.15.3" + bin: + mjml: bin/mjml + checksum: 10c0/e4ccd0111dcbf7b9cba0ef899a68747e02cdae3ff125ee628184d594b595649db501076b86ed41f9ecffca1f62c8626d44c5fcca2375e5c83cef7c6e4351a784 + languageName: node + linkType: hard + +"mkdirp@npm:^0.5.4": + version: 0.5.6 + resolution: "mkdirp@npm:0.5.6" + dependencies: + minimist: "npm:^1.2.6" + bin: + mkdirp: bin/cmd.js + checksum: 10c0/e2e2be789218807b58abced04e7b49851d9e46e88a2f9539242cc8a92c9b5c3a0b9bab360bd3014e02a140fc4fbc58e31176c408b493f8a2a6f4986bd7527b01 + languageName: node + linkType: hard + +"mkdirp@npm:^1.0.3, mkdirp@npm:^1.0.4": + version: 1.0.4 + resolution: "mkdirp@npm:1.0.4" + bin: + mkdirp: bin/cmd.js + checksum: 10c0/46ea0f3ffa8bc6a5bc0c7081ffc3907777f0ed6516888d40a518c5111f8366d97d2678911ad1a6882bf592fa9de6c784fea32e1687bb94e1f4944170af48a5cf + languageName: node + linkType: hard + +"modify-values@npm:^1.0.1": + version: 1.0.1 + resolution: "modify-values@npm:1.0.1" + checksum: 10c0/6acb1b82aaf7a02f9f7b554b20cbfc159f223a79c66b0a257511c5933d50b85e12ea1220b0a90a2af6f80bc29ff784f929a52a51881867a93ae6a12ce87a729a + languageName: node + linkType: hard + +"morgan@npm:^1.10.0": + version: 1.10.0 + resolution: "morgan@npm:1.10.0" + dependencies: + basic-auth: "npm:~2.0.1" + debug: "npm:2.6.9" + depd: "npm:~2.0.0" + on-finished: "npm:~2.3.0" + on-headers: "npm:~1.0.2" + checksum: 10c0/684db061daca28f8d8e3bfd50bd0d21734401b46f74ea76f6df7785d45698fcd98f6d3b81a6bad59f8288c429183afba728c428e8f66d2e8c30fd277af3b5b3a + languageName: node + linkType: hard + +"ms@npm:2.0.0": + version: 2.0.0 + resolution: "ms@npm:2.0.0" + checksum: 10c0/f8fda810b39fd7255bbdc451c46286e549794fcc700dc9cd1d25658bbc4dc2563a5de6fe7c60f798a16a60c6ceb53f033cb353f493f0cf63e5199b702943159d + languageName: node + linkType: hard + +"ms@npm:2.1.2": + version: 2.1.2 + resolution: "ms@npm:2.1.2" + checksum: 10c0/a437714e2f90dbf881b5191d35a6db792efbca5badf112f87b9e1c712aace4b4b9b742dd6537f3edf90fd6f684de897cec230abde57e87883766712ddda297cc + languageName: node + linkType: hard + +"ms@npm:2.1.3, ms@npm:^2.1.1": + version: 2.1.3 + resolution: "ms@npm:2.1.3" + checksum: 10c0/d924b57e7312b3b63ad21fc5b3dc0af5e78d61a1fc7cfb5457edaf26326bf62be5307cc87ffb6862ef1c2b33b0233cdb5d4f01c4c958cc0d660948b65a287a48 + languageName: node + linkType: hard + +"multer@npm:^1.4.5-lts.1": + version: 1.4.5-lts.1 + resolution: "multer@npm:1.4.5-lts.1" + dependencies: + append-field: "npm:^1.0.0" + busboy: "npm:^1.0.0" + concat-stream: "npm:^1.5.2" + mkdirp: "npm:^0.5.4" + object-assign: "npm:^4.1.1" + type-is: "npm:^1.6.4" + xtend: "npm:^4.0.0" + checksum: 10c0/4c6c91e93e510c99e791b6520e3e2f4a227a57f4f509427ff7f3a6f4cc0b4b09ad77c475f629c12f7ae01dba11645b2bd6568877cab775de8bf853b0a67259b4 + languageName: node + linkType: hard + +"multimatch@npm:5.0.0": + version: 5.0.0 + resolution: "multimatch@npm:5.0.0" + dependencies: + "@types/minimatch": "npm:^3.0.3" + array-differ: "npm:^3.0.0" + array-union: "npm:^2.1.0" + arrify: "npm:^2.0.1" + minimatch: "npm:^3.0.4" + checksum: 10c0/252ffae6d19491c169c22fc30cf8a99f6031f94a3495f187d3430b06200e9f05a7efae90ab9d834f090834e0d9c979ab55e7ad21f61a37995d807b4b0ccdcbd1 + languageName: node + linkType: hard + +"mute-stream@npm:0.0.8": + version: 0.0.8 + resolution: "mute-stream@npm:0.0.8" + checksum: 10c0/18d06d92e5d6d45e2b63c0e1b8f25376af71748ac36f53c059baa8b76ffac31c5ab225480494e7d35d30215ecdb18fed26ec23cafcd2f7733f2f14406bcd19e2 + languageName: node + linkType: hard + +"mute-stream@npm:^1.0.0": + version: 1.0.0 + resolution: "mute-stream@npm:1.0.0" + checksum: 10c0/dce2a9ccda171ec979a3b4f869a102b1343dee35e920146776780de182f16eae459644d187e38d59a3d37adf85685e1c17c38cf7bfda7e39a9880f7a1d10a74c + languageName: node + linkType: hard + +"mz@npm:^2.7.0": + version: 2.7.0 + resolution: "mz@npm:2.7.0" + dependencies: + any-promise: "npm:^1.0.0" + object-assign: "npm:^4.0.1" + thenify-all: "npm:^1.0.0" + checksum: 10c0/103114e93f87362f0b56ab5b2e7245051ad0276b646e3902c98397d18bb8f4a77f2ea4a2c9d3ad516034ea3a56553b60d3f5f78220001ca4c404bd711bd0af39 + languageName: node + linkType: hard + +"nanoid@npm:^3.3.6, nanoid@npm:^3.3.7": + version: 3.3.7 + resolution: "nanoid@npm:3.3.7" + bin: + nanoid: bin/nanoid.cjs + checksum: 10c0/e3fb661aa083454f40500473bb69eedb85dc160e763150b9a2c567c7e9ff560ce028a9f833123b618a6ea742e311138b591910e795614a629029e86e180660f3 + languageName: node + linkType: hard + +"negotiator@npm:0.6.3, negotiator@npm:^0.6.3": + version: 0.6.3 + resolution: "negotiator@npm:0.6.3" + checksum: 10c0/3ec9fd413e7bf071c937ae60d572bc67155262068ed522cf4b3be5edbe6ddf67d095ec03a3a14ebf8fc8e95f8e1d61be4869db0dbb0de696f6b837358bd43fc2 + languageName: node + linkType: hard + +"neo-async@npm:^2.6.2": + version: 2.6.2 + resolution: "neo-async@npm:2.6.2" + checksum: 10c0/c2f5a604a54a8ec5438a342e1f356dff4bc33ccccdb6dc668d94fe8e5eccfc9d2c2eea6064b0967a767ba63b33763f51ccf2cd2441b461a7322656c1f06b3f5d + languageName: node + linkType: hard + +"next-seo@npm:^6.5.0": + version: 6.5.0 + resolution: "next-seo@npm:6.5.0" + peerDependencies: + next: ^8.1.1-canary.54 || >=9.0.0 + react: ">=16.0.0" + react-dom: ">=16.0.0" + checksum: 10c0/f2403356aa7fa91314fb91f9b1f7a3436ff76307e2345faec67132e8c0546312f4c6262bc10db28339612c1777dc07ba566bd407262d662f2e417932563837a6 + languageName: node + linkType: hard + +"next@npm:14.2.5": + version: 14.2.5 + resolution: "next@npm:14.2.5" + dependencies: + "@next/env": "npm:14.2.5" + "@next/swc-darwin-arm64": "npm:14.2.5" + "@next/swc-darwin-x64": "npm:14.2.5" + "@next/swc-linux-arm64-gnu": "npm:14.2.5" + "@next/swc-linux-arm64-musl": "npm:14.2.5" + "@next/swc-linux-x64-gnu": "npm:14.2.5" + "@next/swc-linux-x64-musl": "npm:14.2.5" + "@next/swc-win32-arm64-msvc": "npm:14.2.5" + "@next/swc-win32-ia32-msvc": "npm:14.2.5" + "@next/swc-win32-x64-msvc": "npm:14.2.5" + "@swc/helpers": "npm:0.5.5" + busboy: "npm:1.6.0" + caniuse-lite: "npm:^1.0.30001579" + graceful-fs: "npm:^4.2.11" + postcss: "npm:8.4.31" + styled-jsx: "npm:5.1.1" + peerDependencies: + "@opentelemetry/api": ^1.1.0 + "@playwright/test": ^1.41.2 + react: ^18.2.0 + react-dom: ^18.2.0 + sass: ^1.3.0 + dependenciesMeta: + "@next/swc-darwin-arm64": + optional: true + "@next/swc-darwin-x64": + optional: true + "@next/swc-linux-arm64-gnu": + optional: true + "@next/swc-linux-arm64-musl": + optional: true + "@next/swc-linux-x64-gnu": + optional: true + "@next/swc-linux-x64-musl": + optional: true + "@next/swc-win32-arm64-msvc": + optional: true + "@next/swc-win32-ia32-msvc": + optional: true + "@next/swc-win32-x64-msvc": + optional: true + peerDependenciesMeta: + "@opentelemetry/api": + optional: true + "@playwright/test": + optional: true + sass: + optional: true + bin: + next: dist/bin/next + checksum: 10c0/8df7d8ccc1a5bab03fa50dd6656c8a6f3750e81ef0b087dc329fea9346847c3094a933a890a8e87151dc32f0bc55020b8f6386d4565856d83bcc10895d29ec08 + languageName: node + linkType: hard + +"no-case@npm:^2.2.0": + version: 2.3.2 + resolution: "no-case@npm:2.3.2" + dependencies: + lower-case: "npm:^1.1.1" + checksum: 10c0/63f306e83c18efa0bb37f1c23a25baf4ccf5ebaec70b482fa04d4c5bf8bbb8bcc9a8fbcd818af828ab69f2b602153daf81ec26e448b2bda2d704b8d0c7eec8fa + languageName: node + linkType: hard + +"no-case@npm:^3.0.4": + version: 3.0.4 + resolution: "no-case@npm:3.0.4" + dependencies: + lower-case: "npm:^2.0.2" + tslib: "npm:^2.0.3" + checksum: 10c0/8ef545f0b3f8677c848f86ecbd42ca0ff3cd9dd71c158527b344c69ba14710d816d8489c746b6ca225e7b615108938a0bda0a54706f8c255933703ac1cf8e703 + languageName: node + linkType: hard + +"node-addon-api@npm:^5.0.0": + version: 5.1.0 + resolution: "node-addon-api@npm:5.1.0" + dependencies: + node-gyp: "npm:latest" + checksum: 10c0/0eb269786124ba6fad9df8007a149e03c199b3e5a3038125dfb3e747c2d5113d406a4e33f4de1ea600aa2339be1f137d55eba1a73ee34e5fff06c52a5c296d1d + languageName: node + linkType: hard + +"node-cron@npm:^3.0.3": + version: 3.0.3 + resolution: "node-cron@npm:3.0.3" + dependencies: + uuid: "npm:8.3.2" + checksum: 10c0/e6e817c5bf28cca69f256ecc1a2cb737cf895cb0fe22f8ec58ce614cb74dcc22e93f495984a26ece51322473238ccc0ef53b4f373035697017b8c28bddacb9b9 + languageName: node + linkType: hard + +"node-fetch@npm:2.6.7": + version: 2.6.7 + resolution: "node-fetch@npm:2.6.7" + dependencies: + whatwg-url: "npm:^5.0.0" + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + checksum: 10c0/fcae80f5ac52fbf5012f5e19df2bd3915e67d3b3ad51cb5942943df2238d32ba15890fecabd0e166876a9f98a581ab50f3f10eb942b09405c49ef8da36b826c7 + languageName: node + linkType: hard + +"node-fetch@npm:^2.6.0, node-fetch@npm:^2.6.7": + version: 2.7.0 + resolution: "node-fetch@npm:2.7.0" + dependencies: + whatwg-url: "npm:^5.0.0" + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + checksum: 10c0/b55786b6028208e6fbe594ccccc213cab67a72899c9234eb59dba51062a299ea853210fcf526998eaa2867b0963ad72338824450905679ff0fa304b8c5093ae8 + languageName: node + linkType: hard + +"node-gyp@npm:^10.0.0, node-gyp@npm:latest": + version: 10.2.0 + resolution: "node-gyp@npm:10.2.0" + dependencies: + env-paths: "npm:^2.2.0" + exponential-backoff: "npm:^3.1.1" + glob: "npm:^10.3.10" + graceful-fs: "npm:^4.2.6" + make-fetch-happen: "npm:^13.0.0" + nopt: "npm:^7.0.0" + proc-log: "npm:^4.1.0" + semver: "npm:^7.3.5" + tar: "npm:^6.2.1" + which: "npm:^4.0.0" + bin: + node-gyp: bin/node-gyp.js + checksum: 10c0/00630d67dbd09a45aee0a5d55c05e3916ca9e6d427ee4f7bc392d2d3dc5fad7449b21fc098dd38260a53d9dcc9c879b36704a1994235d4707e7271af7e9a835b + languageName: node + linkType: hard + +"node-machine-id@npm:1.1.12": + version: 1.1.12 + resolution: "node-machine-id@npm:1.1.12" + checksum: 10c0/ab2fea5f75a6f1ce3c76c5e0ae3903b631230e0a99b003d176568fff8ddbdf7b2943be96cd8d220c497ca0f6149411831f8a450601929f326781cb1b59bab7f8 + languageName: node + linkType: hard + +"node-releases@npm:^2.0.14": + version: 2.0.17 + resolution: "node-releases@npm:2.0.17" + checksum: 10c0/8b8af0ade683e89e1a9c379e3a2d89604c9ae767ef1e52aa5029ca2db1fab5f741140d04cdbf204f56e4a789e319908988291fb22015f8e9f784b7ea9b1b3dd3 + languageName: node + linkType: hard + +"nopt@npm:^5.0.0": + version: 5.0.0 + resolution: "nopt@npm:5.0.0" + dependencies: + abbrev: "npm:1" + bin: + nopt: bin/nopt.js + checksum: 10c0/fc5c4f07155cb455bf5fc3dd149fac421c1a40fd83c6bfe83aa82b52f02c17c5e88301321318adaa27611c8a6811423d51d29deaceab5fa158b585a61a551061 + languageName: node + linkType: hard + +"nopt@npm:^7.0.0, nopt@npm:^7.2.0, nopt@npm:^7.2.1": + version: 7.2.1 + resolution: "nopt@npm:7.2.1" + dependencies: + abbrev: "npm:^2.0.0" + bin: + nopt: bin/nopt.js + checksum: 10c0/a069c7c736767121242037a22a788863accfa932ab285a1eb569eb8cd534b09d17206f68c37f096ae785647435e0c5a5a0a67b42ec743e481a455e5ae6a6df81 + languageName: node + linkType: hard + +"normalize-package-data@npm:^2.3.2, normalize-package-data@npm:^2.5.0": + version: 2.5.0 + resolution: "normalize-package-data@npm:2.5.0" + dependencies: + hosted-git-info: "npm:^2.1.4" + resolve: "npm:^1.10.0" + semver: "npm:2 || 3 || 4 || 5" + validate-npm-package-license: "npm:^3.0.1" + checksum: 10c0/357cb1646deb42f8eb4c7d42c4edf0eec312f3628c2ef98501963cc4bbe7277021b2b1d977f982b2edce78f5a1014613ce9cf38085c3df2d76730481357ca504 + languageName: node + linkType: hard + +"normalize-package-data@npm:^3.0.0, normalize-package-data@npm:^3.0.3": + version: 3.0.3 + resolution: "normalize-package-data@npm:3.0.3" + dependencies: + hosted-git-info: "npm:^4.0.1" + is-core-module: "npm:^2.5.0" + semver: "npm:^7.3.4" + validate-npm-package-license: "npm:^3.0.1" + checksum: 10c0/e5d0f739ba2c465d41f77c9d950e291ea4af78f8816ddb91c5da62257c40b76d8c83278b0d08ffbcd0f187636ebddad20e181e924873916d03e6e5ea2ef026be + languageName: node + linkType: hard + +"normalize-package-data@npm:^6.0.0, normalize-package-data@npm:^6.0.1": + version: 6.0.2 + resolution: "normalize-package-data@npm:6.0.2" + dependencies: + hosted-git-info: "npm:^7.0.0" + semver: "npm:^7.3.5" + validate-npm-package-license: "npm:^3.0.4" + checksum: 10c0/7e32174e7f5575ede6d3d449593247183880122b4967d4ae6edb28cea5769ca025defda54fc91ec0e3c972fdb5ab11f9284606ba278826171b264cb16a9311ef + languageName: node + linkType: hard + +"normalize-path@npm:^3.0.0, normalize-path@npm:~3.0.0": + version: 3.0.0 + resolution: "normalize-path@npm:3.0.0" + checksum: 10c0/e008c8142bcc335b5e38cf0d63cfd39d6cf2d97480af9abdbe9a439221fd4d749763bab492a8ee708ce7a194bb00c9da6d0a115018672310850489137b3da046 + languageName: node + linkType: hard + +"normalize-range@npm:^0.1.2": + version: 0.1.2 + resolution: "normalize-range@npm:0.1.2" + checksum: 10c0/bf39b73a63e0a42ad1a48c2bd1bda5a07ede64a7e2567307a407674e595bcff0fa0d57e8e5f1e7fa5e91000797c7615e13613227aaaa4d6d6e87f5bd5cc95de6 + languageName: node + linkType: hard + +"npm-bundled@npm:^3.0.0": + version: 3.0.1 + resolution: "npm-bundled@npm:3.0.1" + dependencies: + npm-normalize-package-bin: "npm:^3.0.0" + checksum: 10c0/7975590a50b7ce80dd9f3eddc87f7e990c758f2f2c4d9313dd67a9aca38f1a5ac0abe20d514b850902c441e89d2346adfc3c6f1e9cbab3ea28ebb653c4442440 + languageName: node + linkType: hard + +"npm-install-checks@npm:^6.0.0, npm-install-checks@npm:^6.2.0": + version: 6.3.0 + resolution: "npm-install-checks@npm:6.3.0" + dependencies: + semver: "npm:^7.1.1" + checksum: 10c0/b046ef1de9b40f5d3a9831ce198e1770140a1c3f253dae22eb7b06045191ef79f18f1dcc15a945c919b3c161426861a28050abd321bf439190185794783b6452 + languageName: node + linkType: hard + +"npm-normalize-package-bin@npm:^3.0.0": + version: 3.0.1 + resolution: "npm-normalize-package-bin@npm:3.0.1" + checksum: 10c0/f1831a7f12622840e1375c785c3dab7b1d82dd521211c17ee5e9610cd1a34d8b232d3fdeebf50c170eddcb321d2c644bf73dbe35545da7d588c6b3fa488db0a5 + languageName: node + linkType: hard + +"npm-package-arg@npm:11.0.2, npm-package-arg@npm:^11.0.0, npm-package-arg@npm:^11.0.2": + version: 11.0.2 + resolution: "npm-package-arg@npm:11.0.2" + dependencies: + hosted-git-info: "npm:^7.0.0" + proc-log: "npm:^4.0.0" + semver: "npm:^7.3.5" + validate-npm-package-name: "npm:^5.0.0" + checksum: 10c0/d730572e128980db45c97c184a454cb565283bf849484bf92e3b4e8ec2d08a21bd4b2cba9467466853add3e8c7d81e5de476904ac241f3ae63e6905dfc8196d4 + languageName: node + linkType: hard + +"npm-packlist@npm:8.0.2, npm-packlist@npm:^8.0.0": + version: 8.0.2 + resolution: "npm-packlist@npm:8.0.2" + dependencies: + ignore-walk: "npm:^6.0.4" + checksum: 10c0/ac3140980b1475c2e9acd3d0ca1acd0f8660c357aed357f1a4ebff2270975e0280a3b1c4938e2f16bd68217853ceb5725cf8779ec3752dfcc546582751ceedff + languageName: node + linkType: hard + +"npm-pick-manifest@npm:^9.0.0, npm-pick-manifest@npm:^9.0.1": + version: 9.1.0 + resolution: "npm-pick-manifest@npm:9.1.0" + dependencies: + npm-install-checks: "npm:^6.0.0" + npm-normalize-package-bin: "npm:^3.0.0" + npm-package-arg: "npm:^11.0.0" + semver: "npm:^7.3.5" + checksum: 10c0/8765f4199755b381323da2bff2202b4b15b59f59dba0d1be3f2f793b591321cd19e1b5a686ef48d9753a6bd4868550da632541a45dfb61809d55664222d73e44 + languageName: node + linkType: hard + +"npm-registry-fetch@npm:^17.0.0, npm-registry-fetch@npm:^17.0.1, npm-registry-fetch@npm:^17.1.0": + version: 17.1.0 + resolution: "npm-registry-fetch@npm:17.1.0" + dependencies: + "@npmcli/redact": "npm:^2.0.0" + jsonparse: "npm:^1.3.1" + make-fetch-happen: "npm:^13.0.0" + minipass: "npm:^7.0.2" + minipass-fetch: "npm:^3.0.0" + minizlib: "npm:^2.1.2" + npm-package-arg: "npm:^11.0.0" + proc-log: "npm:^4.0.0" + checksum: 10c0/3f66214e106609fd2e92704e62ac929cba1424d4013fec50f783afbb81168b0dc14457d35c1716a77e30fc482c3576bdc4e4bc5c84a714cac59cf98f96a17f47 + languageName: node + linkType: hard + +"npm-run-path@npm:^4.0.1": + version: 4.0.1 + resolution: "npm-run-path@npm:4.0.1" + dependencies: + path-key: "npm:^3.0.0" + checksum: 10c0/6f9353a95288f8455cf64cbeb707b28826a7f29690244c1e4bb61ec573256e021b6ad6651b394eb1ccfd00d6ec50147253aba2c5fe58a57ceb111fad62c519ac + languageName: node + linkType: hard + +"npmlog@npm:^5.0.1": + version: 5.0.1 + resolution: "npmlog@npm:5.0.1" + dependencies: + are-we-there-yet: "npm:^2.0.0" + console-control-strings: "npm:^1.1.0" + gauge: "npm:^3.0.0" + set-blocking: "npm:^2.0.0" + checksum: 10c0/489ba519031013001135c463406f55491a17fc7da295c18a04937fe3a4d523fd65e88dd418a28b967ab743d913fdeba1e29838ce0ad8c75557057c481f7d49fa + languageName: node + linkType: hard + +"nprogress@npm:^0.2.0": + version: 0.2.0 + resolution: "nprogress@npm:0.2.0" + checksum: 10c0/eab9a923a1ad1eed71a455ecfbc358442dd9bcd71b9fa3fa1c67eddf5159360b182c218f76fca320c97541a1b45e19ced04e6dcb044a662244c5419f8ae9e821 + languageName: node + linkType: hard + +"nth-check@npm:^2.0.1": + version: 2.1.1 + resolution: "nth-check@npm:2.1.1" + dependencies: + boolbase: "npm:^1.0.0" + checksum: 10c0/5fee7ff309727763689cfad844d979aedd2204a817fbaaf0e1603794a7c20db28548d7b024692f953557df6ce4a0ee4ae46cd8ebd9b36cfb300b9226b567c479 + languageName: node + linkType: hard + +"nx@npm:19.5.1, nx@npm:>=17.1.2 < 20": + version: 19.5.1 + resolution: "nx@npm:19.5.1" + dependencies: + "@napi-rs/wasm-runtime": "npm:0.2.4" + "@nrwl/tao": "npm:19.5.1" + "@nx/nx-darwin-arm64": "npm:19.5.1" + "@nx/nx-darwin-x64": "npm:19.5.1" + "@nx/nx-freebsd-x64": "npm:19.5.1" + "@nx/nx-linux-arm-gnueabihf": "npm:19.5.1" + "@nx/nx-linux-arm64-gnu": "npm:19.5.1" + "@nx/nx-linux-arm64-musl": "npm:19.5.1" + "@nx/nx-linux-x64-gnu": "npm:19.5.1" + "@nx/nx-linux-x64-musl": "npm:19.5.1" + "@nx/nx-win32-arm64-msvc": "npm:19.5.1" + "@nx/nx-win32-x64-msvc": "npm:19.5.1" + "@yarnpkg/lockfile": "npm:^1.1.0" + "@yarnpkg/parsers": "npm:3.0.0-rc.46" + "@zkochan/js-yaml": "npm:0.0.7" + axios: "npm:^1.6.0" + chalk: "npm:^4.1.0" + cli-cursor: "npm:3.1.0" + cli-spinners: "npm:2.6.1" + cliui: "npm:^8.0.1" + dotenv: "npm:~16.4.5" + dotenv-expand: "npm:~11.0.6" + enquirer: "npm:~2.3.6" + figures: "npm:3.2.0" + flat: "npm:^5.0.2" + front-matter: "npm:^4.0.2" + fs-extra: "npm:^11.1.0" + ignore: "npm:^5.0.4" + jest-diff: "npm:^29.4.1" + jsonc-parser: "npm:3.2.0" + lines-and-columns: "npm:~2.0.3" + minimatch: "npm:9.0.3" + node-machine-id: "npm:1.1.12" + npm-run-path: "npm:^4.0.1" + open: "npm:^8.4.0" + ora: "npm:5.3.0" + semver: "npm:^7.5.3" + string-width: "npm:^4.2.3" + strong-log-transformer: "npm:^2.1.0" + tar-stream: "npm:~2.2.0" + tmp: "npm:~0.2.1" + tsconfig-paths: "npm:^4.1.2" + tslib: "npm:^2.3.0" + yargs: "npm:^17.6.2" + yargs-parser: "npm:21.1.1" + peerDependencies: + "@swc-node/register": ^1.8.0 + "@swc/core": ^1.3.85 + dependenciesMeta: + "@nx/nx-darwin-arm64": + optional: true + "@nx/nx-darwin-x64": + optional: true + "@nx/nx-freebsd-x64": + optional: true + "@nx/nx-linux-arm-gnueabihf": + optional: true + "@nx/nx-linux-arm64-gnu": + optional: true + "@nx/nx-linux-arm64-musl": + optional: true + "@nx/nx-linux-x64-gnu": + optional: true + "@nx/nx-linux-x64-musl": + optional: true + "@nx/nx-win32-arm64-msvc": + optional: true + "@nx/nx-win32-x64-msvc": + optional: true + peerDependenciesMeta: + "@swc-node/register": + optional: true + "@swc/core": + optional: true + bin: + nx: bin/nx.js + nx-cloud: bin/nx-cloud.js + checksum: 10c0/8be20b5d2897784ae8b46b2c273788076f122c8e9d4d33fcfc8b8210640d06dd0360ec2bfaf4a4761406c96fce8b34e78e26c452118bcb85bf6d6c3d8a750c54 + languageName: node + linkType: hard + +"object-assign@npm:^4, object-assign@npm:^4.0.1, object-assign@npm:^4.1.1": + version: 4.1.1 + resolution: "object-assign@npm:4.1.1" + checksum: 10c0/1f4df9945120325d041ccf7b86f31e8bcc14e73d29171e37a7903050e96b81323784ec59f93f102ec635bcf6fa8034ba3ea0a8c7e69fa202b87ae3b6cec5a414 + languageName: node + linkType: hard + +"object-hash@npm:^3.0.0": + version: 3.0.0 + resolution: "object-hash@npm:3.0.0" + checksum: 10c0/a06844537107b960c1c8b96cd2ac8592a265186bfa0f6ccafe0d34eabdb526f6fa81da1f37c43df7ed13b12a4ae3457a16071603bcd39d8beddb5f08c37b0f47 + languageName: node + linkType: hard + +"object-inspect@npm:^1.13.1": + version: 1.13.2 + resolution: "object-inspect@npm:1.13.2" + checksum: 10c0/b97835b4c91ec37b5fd71add84f21c3f1047d1d155d00c0fcd6699516c256d4fcc6ff17a1aced873197fe447f91a3964178fd2a67a1ee2120cdaf60e81a050b4 + languageName: node + linkType: hard + +"on-finished@npm:2.4.1": + version: 2.4.1 + resolution: "on-finished@npm:2.4.1" + dependencies: + ee-first: "npm:1.1.1" + checksum: 10c0/46fb11b9063782f2d9968863d9cbba33d77aa13c17f895f56129c274318b86500b22af3a160fe9995aa41317efcd22941b6eba747f718ced08d9a73afdb087b4 + languageName: node + linkType: hard + +"on-finished@npm:~2.3.0": + version: 2.3.0 + resolution: "on-finished@npm:2.3.0" + dependencies: + ee-first: "npm:1.1.1" + checksum: 10c0/c904f9e518b11941eb60279a3cbfaf1289bd0001f600a950255b1dede9fe3df8cd74f38483550b3bb9485165166acb5db500c3b4c4337aec2815c88c96fcc2ea + languageName: node + linkType: hard + +"on-headers@npm:~1.0.2": + version: 1.0.2 + resolution: "on-headers@npm:1.0.2" + checksum: 10c0/f649e65c197bf31505a4c0444875db0258e198292f34b884d73c2f751e91792ef96bb5cf89aa0f4fecc2e4dc662461dda606b1274b0e564f539cae5d2f5fc32f + languageName: node + linkType: hard + +"once@npm:^1.3.0, once@npm:^1.4.0": + version: 1.4.0 + resolution: "once@npm:1.4.0" + dependencies: + wrappy: "npm:1" + checksum: 10c0/5d48aca287dfefabd756621c5dfce5c91a549a93e9fdb7b8246bc4c4790aa2ec17b34a260530474635147aeb631a2dcc8b32c613df0675f96041cbb8244517d0 + languageName: node + linkType: hard + +"onetime@npm:^5.1.0, onetime@npm:^5.1.2": + version: 5.1.2 + resolution: "onetime@npm:5.1.2" + dependencies: + mimic-fn: "npm:^2.1.0" + checksum: 10c0/ffcef6fbb2692c3c40749f31ea2e22677a876daea92959b8a80b521d95cca7a668c884d8b2045d1d8ee7d56796aa405c405462af112a1477594cc63531baeb8f + languageName: node + linkType: hard + +"open@npm:^8.4.0": + version: 8.4.2 + resolution: "open@npm:8.4.2" + dependencies: + define-lazy-prop: "npm:^2.0.0" + is-docker: "npm:^2.1.1" + is-wsl: "npm:^2.2.0" + checksum: 10c0/bb6b3a58401dacdb0aad14360626faf3fb7fba4b77816b373495988b724fb48941cad80c1b65d62bb31a17609b2cd91c41a181602caea597ca80dfbcc27e84c9 + languageName: node + linkType: hard + +"ora@npm:5.3.0": + version: 5.3.0 + resolution: "ora@npm:5.3.0" + dependencies: + bl: "npm:^4.0.3" + chalk: "npm:^4.1.0" + cli-cursor: "npm:^3.1.0" + cli-spinners: "npm:^2.5.0" + is-interactive: "npm:^1.0.0" + log-symbols: "npm:^4.0.0" + strip-ansi: "npm:^6.0.0" + wcwidth: "npm:^1.0.1" + checksum: 10c0/30d5f3218eb75b0a2028c5fb9aa88e83e38a2f1745ab56839abb06c3ba31bae35f768f4e72c4f9e04e2a66be6a898e9312e8cf85c9333e1e3613eabb8c7cdf57 + languageName: node + linkType: hard + +"ora@npm:^5.4.1": + version: 5.4.1 + resolution: "ora@npm:5.4.1" + dependencies: + bl: "npm:^4.1.0" + chalk: "npm:^4.1.0" + cli-cursor: "npm:^3.1.0" + cli-spinners: "npm:^2.5.0" + is-interactive: "npm:^1.0.0" + is-unicode-supported: "npm:^0.1.0" + log-symbols: "npm:^4.1.0" + strip-ansi: "npm:^6.0.0" + wcwidth: "npm:^1.0.1" + checksum: 10c0/10ff14aace236d0e2f044193362b22edce4784add08b779eccc8f8ef97195cae1248db8ec1ec5f5ff076f91acbe573f5f42a98c19b78dba8c54eefff983cae85 + languageName: node + linkType: hard + +"orderedmap@npm:^2.0.0": + version: 2.1.1 + resolution: "orderedmap@npm:2.1.1" + checksum: 10c0/8d7d266659d1828937046e8b2a7b5f75914e0391db985da0ca75cd2246cccbf6d6f3a0886aa2034da15ee4923e8c45f95f8b588f575f535f0adecdefccc54634 + languageName: node + linkType: hard + +"os-tmpdir@npm:~1.0.2": + version: 1.0.2 + resolution: "os-tmpdir@npm:1.0.2" + checksum: 10c0/f438450224f8e2687605a8dd318f0db694b6293c5d835ae509a69e97c8de38b6994645337e5577f5001115470414638978cc49da1cdcc25106dad8738dc69990 + languageName: node + linkType: hard + +"p-finally@npm:^1.0.0": + version: 1.0.0 + resolution: "p-finally@npm:1.0.0" + checksum: 10c0/6b8552339a71fe7bd424d01d8451eea92d379a711fc62f6b2fe64cad8a472c7259a236c9a22b4733abca0b5666ad503cb497792a0478c5af31ded793d00937e7 + languageName: node + linkType: hard + +"p-limit@npm:^1.1.0": + version: 1.3.0 + resolution: "p-limit@npm:1.3.0" + dependencies: + p-try: "npm:^1.0.0" + checksum: 10c0/5c1b1d53d180b2c7501efb04b7c817448e10efe1ba46f4783f8951994d5027e4cd88f36ad79af50546682594c4ebd11702ac4b9364c47f8074890e2acad0edee + languageName: node + linkType: hard + +"p-limit@npm:^2.2.0": + version: 2.3.0 + resolution: "p-limit@npm:2.3.0" + dependencies: + p-try: "npm:^2.0.0" + checksum: 10c0/8da01ac53efe6a627080fafc127c873da40c18d87b3f5d5492d465bb85ec7207e153948df6b9cbaeb130be70152f874229b8242ee2be84c0794082510af97f12 + languageName: node + linkType: hard + +"p-locate@npm:^2.0.0": + version: 2.0.0 + resolution: "p-locate@npm:2.0.0" + dependencies: + p-limit: "npm:^1.1.0" + checksum: 10c0/82da4be88fb02fd29175e66021610c881938d3cc97c813c71c1a605fac05617d57fd5d3b337494a6106c0edb2a37c860241430851411f1b265108cead34aee67 + languageName: node + linkType: hard + +"p-locate@npm:^4.1.0": + version: 4.1.0 + resolution: "p-locate@npm:4.1.0" + dependencies: + p-limit: "npm:^2.2.0" + checksum: 10c0/1b476ad69ad7f6059744f343b26d51ce091508935c1dbb80c4e0a2f397ffce0ca3a1f9f5cd3c7ce19d7929a09719d5c65fe70d8ee289c3f267cd36f2881813e9 + languageName: node + linkType: hard + +"p-map-series@npm:2.1.0": + version: 2.1.0 + resolution: "p-map-series@npm:2.1.0" + checksum: 10c0/302ca686a61c498b227fc45d4e2b2e5bfd20a03f4156a976d94c4ff7decf9cd5a815fa6846b43b37d587ffa8d4671ff2bd596fa83fe8b9113b5102da94940e2a + languageName: node + linkType: hard + +"p-map@npm:4.0.0, p-map@npm:^4.0.0": + version: 4.0.0 + resolution: "p-map@npm:4.0.0" + dependencies: + aggregate-error: "npm:^3.0.0" + checksum: 10c0/592c05bd6262c466ce269ff172bb8de7c6975afca9b50c975135b974e9bdaafbfe80e61aaaf5be6d1200ba08b30ead04b88cfa7e25ff1e3b93ab28c9f62a2c75 + languageName: node + linkType: hard + +"p-pipe@npm:3.1.0": + version: 3.1.0 + resolution: "p-pipe@npm:3.1.0" + checksum: 10c0/9b3076828ea7e9469c0f92c78fa44096726208d547efdb2d6148cbe135d1a70bd449de5be13e234dd669d9515343bd68527b316bf9d5639cee639e2fdde20aaf + languageName: node + linkType: hard + +"p-queue@npm:6.6.2": + version: 6.6.2 + resolution: "p-queue@npm:6.6.2" + dependencies: + eventemitter3: "npm:^4.0.4" + p-timeout: "npm:^3.2.0" + checksum: 10c0/5739ecf5806bbeadf8e463793d5e3004d08bb3f6177bd1a44a005da8fd81bb90f80e4633e1fb6f1dfd35ee663a5c0229abe26aebb36f547ad5a858347c7b0d3e + languageName: node + linkType: hard + +"p-reduce@npm:2.1.0, p-reduce@npm:^2.0.0, p-reduce@npm:^2.1.0": + version: 2.1.0 + resolution: "p-reduce@npm:2.1.0" + checksum: 10c0/27b8ff0fb044995507a06cd6357dffba0f2b98862864745972562a21885d7906ce5c794036d2aaa63ef6303158e41e19aed9f19651dfdafb38548ecec7d0de15 + languageName: node + linkType: hard + +"p-timeout@npm:^3.2.0": + version: 3.2.0 + resolution: "p-timeout@npm:3.2.0" + dependencies: + p-finally: "npm:^1.0.0" + checksum: 10c0/524b393711a6ba8e1d48137c5924749f29c93d70b671e6db761afa784726572ca06149c715632da8f70c090073afb2af1c05730303f915604fd38ee207b70a61 + languageName: node + linkType: hard + +"p-try@npm:^1.0.0": + version: 1.0.0 + resolution: "p-try@npm:1.0.0" + checksum: 10c0/757ba31de5819502b80c447826fac8be5f16d3cb4fbf9bc8bc4971dba0682e84ac33e4b24176ca7058c69e29f64f34d8d9e9b08e873b7b7bb0aa89d620fa224a + languageName: node + linkType: hard + +"p-try@npm:^2.0.0": + version: 2.2.0 + resolution: "p-try@npm:2.2.0" + checksum: 10c0/c36c19907734c904b16994e6535b02c36c2224d433e01a2f1ab777237f4d86e6289fd5fd464850491e940379d4606ed850c03e0f9ab600b0ebddb511312e177f + languageName: node + linkType: hard + +"p-waterfall@npm:2.1.1": + version: 2.1.1 + resolution: "p-waterfall@npm:2.1.1" + dependencies: + p-reduce: "npm:^2.0.0" + checksum: 10c0/ccae582b75a3597018a375f8eac32b93e8bfb9fc22a8e5037787ef4ebf5958d7465c2d3cbe26443971fbbfda2bcb7b645f694b91f928fc9a71fa5031e6e33f85 + languageName: node + linkType: hard + +"package-json-from-dist@npm:^1.0.0": + version: 1.0.0 + resolution: "package-json-from-dist@npm:1.0.0" + checksum: 10c0/e3ffaf6ac1040ab6082a658230c041ad14e72fabe99076a2081bb1d5d41210f11872403fc09082daf4387fc0baa6577f96c9c0e94c90c394fd57794b66aa4033 + languageName: node + linkType: hard + +"pacote@npm:^18.0.0, pacote@npm:^18.0.6": + version: 18.0.6 + resolution: "pacote@npm:18.0.6" + dependencies: + "@npmcli/git": "npm:^5.0.0" + "@npmcli/installed-package-contents": "npm:^2.0.1" + "@npmcli/package-json": "npm:^5.1.0" + "@npmcli/promise-spawn": "npm:^7.0.0" + "@npmcli/run-script": "npm:^8.0.0" + cacache: "npm:^18.0.0" + fs-minipass: "npm:^3.0.0" + minipass: "npm:^7.0.2" + npm-package-arg: "npm:^11.0.0" + npm-packlist: "npm:^8.0.0" + npm-pick-manifest: "npm:^9.0.0" + npm-registry-fetch: "npm:^17.0.0" + proc-log: "npm:^4.0.0" + promise-retry: "npm:^2.0.1" + sigstore: "npm:^2.2.0" + ssri: "npm:^10.0.0" + tar: "npm:^6.1.11" + bin: + pacote: bin/index.js + checksum: 10c0/d80907375dd52a521255e0debca1ba9089ad8fd7acdf16c5a5db2ea2a5bb23045e2bcf08d1648b1ebc40fcc889657db86ff6187ff5f8d2fc312cd6ad1ec4c6ac + languageName: node + linkType: hard + +"param-case@npm:^2.1.1": + version: 2.1.1 + resolution: "param-case@npm:2.1.1" + dependencies: + no-case: "npm:^2.2.0" + checksum: 10c0/8ea1b8472fd51d5f50b28d1d754899713805d05f2241e9b8c4acafa2c500b3f47457a3b4932ab75220f14d2c69180bb7338b78a45576e2b4d90da1e6f0285833 + languageName: node + linkType: hard + +"parent-module@npm:^1.0.0": + version: 1.0.1 + resolution: "parent-module@npm:1.0.1" + dependencies: + callsites: "npm:^3.0.0" + checksum: 10c0/c63d6e80000d4babd11978e0d3fee386ca7752a02b035fd2435960ffaa7219dc42146f07069fb65e6e8bf1caef89daf9af7535a39bddf354d78bf50d8294f556 + languageName: node + linkType: hard + +"parse-conflict-json@npm:^3.0.0": + version: 3.0.1 + resolution: "parse-conflict-json@npm:3.0.1" + dependencies: + json-parse-even-better-errors: "npm:^3.0.0" + just-diff: "npm:^6.0.0" + just-diff-apply: "npm:^5.2.0" + checksum: 10c0/610b37181229ce3e945125c3a9548ec24d1de2d697a7ea3ef0f2660cccc6613715c2ba4bdbaf37c565133d6b61758703618a2c63d1ee29f97fd33c70a8aae323 + languageName: node + linkType: hard + +"parse-entities@npm:^2.0.0": + version: 2.0.0 + resolution: "parse-entities@npm:2.0.0" + dependencies: + character-entities: "npm:^1.0.0" + character-entities-legacy: "npm:^1.0.0" + character-reference-invalid: "npm:^1.0.0" + is-alphanumerical: "npm:^1.0.0" + is-decimal: "npm:^1.0.0" + is-hexadecimal: "npm:^1.0.0" + checksum: 10c0/f85a22c0ea406ff26b53fdc28641f01cc36fa49eb2e3135f02693286c89ef0bcefc2262d99b3688e20aac2a14fd10b75c518583e875c1b9fe3d1f937795e0854 + languageName: node + linkType: hard + +"parse-json@npm:^4.0.0": + version: 4.0.0 + resolution: "parse-json@npm:4.0.0" + dependencies: + error-ex: "npm:^1.3.1" + json-parse-better-errors: "npm:^1.0.1" + checksum: 10c0/8d80790b772ccb1bcea4e09e2697555e519d83d04a77c2b4237389b813f82898943a93ffff7d0d2406203bdd0c30dcf95b1661e3a53f83d0e417f053957bef32 + languageName: node + linkType: hard + +"parse-json@npm:^5.0.0, parse-json@npm:^5.2.0": + version: 5.2.0 + resolution: "parse-json@npm:5.2.0" + dependencies: + "@babel/code-frame": "npm:^7.0.0" + error-ex: "npm:^1.3.1" + json-parse-even-better-errors: "npm:^2.3.0" + lines-and-columns: "npm:^1.1.6" + checksum: 10c0/77947f2253005be7a12d858aedbafa09c9ae39eb4863adf330f7b416ca4f4a08132e453e08de2db46459256fb66afaac5ee758b44fe6541b7cdaf9d252e59585 + languageName: node + linkType: hard + +"parse-path@npm:^7.0.0": + version: 7.0.0 + resolution: "parse-path@npm:7.0.0" + dependencies: + protocols: "npm:^2.0.0" + checksum: 10c0/e7646f6b998b083bbd40102643d803557ce4ae18ae1704e6cc7ae2525ea7c5400f4a3635aca3244cfe65ce4dd0ff77db1142dde4d080e8a80c364c4b3e8fe8d2 + languageName: node + linkType: hard + +"parse-url@npm:^8.1.0": + version: 8.1.0 + resolution: "parse-url@npm:8.1.0" + dependencies: + parse-path: "npm:^7.0.0" + checksum: 10c0/68b95afdf4bbf72e57c7ab66f8757c935fff888f7e2b0f1e06098b4faa19e06b6b743bddaed5bc8df4f0c2de6fc475355d787373b2fdd40092be9e4e4b996648 + languageName: node + linkType: hard + +"parse5-htmlparser2-tree-adapter@npm:^7.0.0": + version: 7.0.0 + resolution: "parse5-htmlparser2-tree-adapter@npm:7.0.0" + dependencies: + domhandler: "npm:^5.0.2" + parse5: "npm:^7.0.0" + checksum: 10c0/e820cacb8486e6f7ede403327d18480df086d70e32ede2f6654d8c3a8b4b8dc4a4d5c21c03c18a92ba2466c513b93ca63be4a138dd73cd0995f384eb3b9edf11 + languageName: node + linkType: hard + +"parse5@npm:^7.0.0": + version: 7.1.2 + resolution: "parse5@npm:7.1.2" + dependencies: + entities: "npm:^4.4.0" + checksum: 10c0/297d7af8224f4b5cb7f6617ecdae98eeaed7f8cbd78956c42785e230505d5a4f07cef352af10d3006fa5c1544b76b57784d3a22d861ae071bbc460c649482bf4 + languageName: node + linkType: hard + +"parseurl@npm:~1.3.3": + version: 1.3.3 + resolution: "parseurl@npm:1.3.3" + checksum: 10c0/90dd4760d6f6174adb9f20cf0965ae12e23879b5f5464f38e92fce8073354341e4b3b76fa3d878351efe7d01e617121955284cfd002ab087fba1a0726ec0b4f5 + languageName: node + linkType: hard + +"path-exists@npm:^3.0.0": + version: 3.0.0 + resolution: "path-exists@npm:3.0.0" + checksum: 10c0/17d6a5664bc0a11d48e2b2127d28a0e58822c6740bde30403f08013da599182289c56518bec89407e3f31d3c2b6b296a4220bc3f867f0911fee6952208b04167 + languageName: node + linkType: hard + +"path-exists@npm:^4.0.0": + version: 4.0.0 + resolution: "path-exists@npm:4.0.0" + checksum: 10c0/8c0bd3f5238188197dc78dced15207a4716c51cc4e3624c44fc97acf69558f5ebb9a2afff486fe1b4ee148e0c133e96c5e11a9aa5c48a3006e3467da070e5e1b + languageName: node + linkType: hard + +"path-is-absolute@npm:^1.0.0": + version: 1.0.1 + resolution: "path-is-absolute@npm:1.0.1" + checksum: 10c0/127da03c82172a2a50099cddbf02510c1791fc2cc5f7713ddb613a56838db1e8168b121a920079d052e0936c23005562059756d653b7c544c53185efe53be078 + languageName: node + linkType: hard + +"path-key@npm:^3.0.0, path-key@npm:^3.1.0": + version: 3.1.1 + resolution: "path-key@npm:3.1.1" + checksum: 10c0/748c43efd5a569c039d7a00a03b58eecd1d75f3999f5a28303d75f521288df4823bc057d8784eb72358b2895a05f29a070bc9f1f17d28226cc4e62494cc58c4c + languageName: node + linkType: hard + +"path-parse@npm:^1.0.7": + version: 1.0.7 + resolution: "path-parse@npm:1.0.7" + checksum: 10c0/11ce261f9d294cc7a58d6a574b7f1b935842355ec66fba3c3fd79e0f036462eaf07d0aa95bb74ff432f9afef97ce1926c720988c6a7451d8a584930ae7de86e1 + languageName: node + linkType: hard + +"path-scurry@npm:^1.11.1, path-scurry@npm:^1.6.1": + version: 1.11.1 + resolution: "path-scurry@npm:1.11.1" + dependencies: + lru-cache: "npm:^10.2.0" + minipass: "npm:^5.0.0 || ^6.0.2 || ^7.0.0" + checksum: 10c0/32a13711a2a505616ae1cc1b5076801e453e7aae6ac40ab55b388bb91b9d0547a52f5aaceff710ea400205f18691120d4431e520afbe4266b836fadede15872d + languageName: node + linkType: hard + +"path-to-regexp@npm:0.1.7": + version: 0.1.7 + resolution: "path-to-regexp@npm:0.1.7" + checksum: 10c0/50a1ddb1af41a9e68bd67ca8e331a705899d16fb720a1ea3a41e310480948387daf603abb14d7b0826c58f10146d49050a1291ba6a82b78a382d1c02c0b8f905 + languageName: node + linkType: hard + +"path-type@npm:^3.0.0": + version: 3.0.0 + resolution: "path-type@npm:3.0.0" + dependencies: + pify: "npm:^3.0.0" + checksum: 10c0/1332c632f1cac15790ebab8dd729b67ba04fc96f81647496feb1c2975d862d046f41e4b975dbd893048999b2cc90721f72924ad820acc58c78507ba7141a8e56 + languageName: node + linkType: hard + +"path-type@npm:^4.0.0": + version: 4.0.0 + resolution: "path-type@npm:4.0.0" + checksum: 10c0/666f6973f332f27581371efaf303fd6c272cc43c2057b37aa99e3643158c7e4b2626549555d88626e99ea9e046f82f32e41bbde5f1508547e9a11b149b52387c + languageName: node + linkType: hard + +"picocolors@npm:^1.0.0, picocolors@npm:^1.0.1": + version: 1.0.1 + resolution: "picocolors@npm:1.0.1" + checksum: 10c0/c63cdad2bf812ef0d66c8db29583802355d4ca67b9285d846f390cc15c2f6ccb94e8cb7eb6a6e97fc5990a6d3ad4ae42d86c84d3146e667c739a4234ed50d400 + languageName: node + linkType: hard + +"picomatch@npm:^2.0.4, picomatch@npm:^2.2.1, picomatch@npm:^2.3.1": + version: 2.3.1 + resolution: "picomatch@npm:2.3.1" + checksum: 10c0/26c02b8d06f03206fc2ab8d16f19960f2ff9e81a658f831ecb656d8f17d9edc799e8364b1f4a7873e89d9702dff96204be0fa26fe4181f6843f040f819dac4be + languageName: node + linkType: hard + +"pify@npm:5.0.0": + version: 5.0.0 + resolution: "pify@npm:5.0.0" + checksum: 10c0/9f6f3cd1f159652692f514383efe401a06473af35a699962230ad1c4c9796df5999961461fc1a3b81eed8e3e74adb8bd032474fb3f93eb6bdbd9f33328da1ed2 + languageName: node + linkType: hard + +"pify@npm:^2.3.0": + version: 2.3.0 + resolution: "pify@npm:2.3.0" + checksum: 10c0/551ff8ab830b1052633f59cb8adc9ae8407a436e06b4a9718bcb27dc5844b83d535c3a8512b388b6062af65a98c49bdc0dd523d8b2617b188f7c8fee457158dc + languageName: node + linkType: hard + +"pify@npm:^3.0.0": + version: 3.0.0 + resolution: "pify@npm:3.0.0" + checksum: 10c0/fead19ed9d801f1b1fcd0638a1ac53eabbb0945bf615f2f8806a8b646565a04a1b0e7ef115c951d225f042cca388fdc1cd3add46d10d1ed6951c20bd2998af10 + languageName: node + linkType: hard + +"pify@npm:^4.0.1": + version: 4.0.1 + resolution: "pify@npm:4.0.1" + checksum: 10c0/6f9d404b0d47a965437403c9b90eca8bb2536407f03de165940e62e72c8c8b75adda5516c6b9b23675a5877cc0bcac6bdfb0ef0e39414cd2476d5495da40e7cf + languageName: node + linkType: hard + +"pirates@npm:^4.0.1": + version: 4.0.6 + resolution: "pirates@npm:4.0.6" + checksum: 10c0/00d5fa51f8dded94d7429700fb91a0c1ead00ae2c7fd27089f0c5b63e6eca36197fe46384631872690a66f390c5e27198e99006ab77ae472692ab9c2ca903f36 + languageName: node + linkType: hard + +"pkg-conf@npm:^2.1.0": + version: 2.1.0 + resolution: "pkg-conf@npm:2.1.0" + dependencies: + find-up: "npm:^2.0.0" + load-json-file: "npm:^4.0.0" + checksum: 10c0/e1474a4f7714ee78204b4a7f2316dec9e59887762bdc126ebd0eb701bbde7c6a6da65c4dc9c2a7c1eaeee49914009bf4a4368f5d9894c596ddf812ff982fdb05 + languageName: node + linkType: hard + +"pkg-dir@npm:^4.2.0": + version: 4.2.0 + resolution: "pkg-dir@npm:4.2.0" + dependencies: + find-up: "npm:^4.0.0" + checksum: 10c0/c56bda7769e04907a88423feb320babaed0711af8c436ce3e56763ab1021ba107c7b0cafb11cde7529f669cfc22bffcaebffb573645cbd63842ea9fb17cd7728 + languageName: node + linkType: hard + +"plunk@workspace:.": + version: 0.0.0-use.local + resolution: "plunk@workspace:." + dependencies: + "@biomejs/biome": "npm:^1.8.3" + "@prisma/client": "npm:^5.17.0" + lerna: "npm:^8.1.6" + prisma: "npm:^5.17.0" + rimraf: "npm:^5.0.9" + languageName: unknown + linkType: soft + +"postcss-import@npm:^15.1.0": + version: 15.1.0 + resolution: "postcss-import@npm:15.1.0" + dependencies: + postcss-value-parser: "npm:^4.0.0" + read-cache: "npm:^1.0.0" + resolve: "npm:^1.1.7" + peerDependencies: + postcss: ^8.0.0 + checksum: 10c0/518aee5c83ea6940e890b0be675a2588db68b2582319f48c3b4e06535a50ea6ee45f7e63e4309f8754473245c47a0372632378d1d73d901310f295a92f26f17b + languageName: node + linkType: hard + +"postcss-js@npm:^4.0.1": + version: 4.0.1 + resolution: "postcss-js@npm:4.0.1" + dependencies: + camelcase-css: "npm:^2.0.1" + peerDependencies: + postcss: ^8.4.21 + checksum: 10c0/af35d55cb873b0797d3b42529514f5318f447b134541844285c9ac31a17497297eb72296902967911bb737a75163441695737300ce2794e3bd8c70c13a3b106e + languageName: node + linkType: hard + +"postcss-load-config@npm:^4.0.1": + version: 4.0.2 + resolution: "postcss-load-config@npm:4.0.2" + dependencies: + lilconfig: "npm:^3.0.0" + yaml: "npm:^2.3.4" + peerDependencies: + postcss: ">=8.0.9" + ts-node: ">=9.0.0" + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + checksum: 10c0/3d7939acb3570b0e4b4740e483d6e555a3e2de815219cb8a3c8fc03f575a6bde667443aa93369c0be390af845cb84471bf623e24af833260de3a105b78d42519 + languageName: node + linkType: hard + +"postcss-nested@npm:^6.0.1": + version: 6.2.0 + resolution: "postcss-nested@npm:6.2.0" + dependencies: + postcss-selector-parser: "npm:^6.1.1" + peerDependencies: + postcss: ^8.2.14 + checksum: 10c0/7f9c3f2d764191a39364cbdcec350f26a312431a569c9ef17408021424726b0d67995ff5288405e3724bb7152a4c92f73c027e580ec91e798800ed3c52e2bc6e + languageName: node + linkType: hard + +"postcss-selector-parser@npm:6.0.10": + version: 6.0.10 + resolution: "postcss-selector-parser@npm:6.0.10" + dependencies: + cssesc: "npm:^3.0.0" + util-deprecate: "npm:^1.0.2" + checksum: 10c0/a0b27c5e3f7604c8dc7cd83f145fdd7b21448e0d86072da99e0d78e536ba27aa9db2d42024c50aa530408ee517c4bdc0260529e1afb56608f9a82e839c207e82 + languageName: node + linkType: hard + +"postcss-selector-parser@npm:^6.0.10, postcss-selector-parser@npm:^6.0.11, postcss-selector-parser@npm:^6.1.1": + version: 6.1.1 + resolution: "postcss-selector-parser@npm:6.1.1" + dependencies: + cssesc: "npm:^3.0.0" + util-deprecate: "npm:^1.0.2" + checksum: 10c0/5608765e033fee35d448e1f607ffbaa750eb86901824a8bc4a911ea8bc137cb82f29239330787427c5d3695afd90d8721e190f211dbbf733e25033d8b3100763 + languageName: node + linkType: hard + +"postcss-value-parser@npm:^4.0.0, postcss-value-parser@npm:^4.2.0": + version: 4.2.0 + resolution: "postcss-value-parser@npm:4.2.0" + checksum: 10c0/f4142a4f56565f77c1831168e04e3effd9ffcc5aebaf0f538eee4b2d465adfd4b85a44257bb48418202a63806a7da7fe9f56c330aebb3cac898e46b4cbf49161 + languageName: node + linkType: hard + +"postcss@npm:8.4.31": + version: 8.4.31 + resolution: "postcss@npm:8.4.31" + dependencies: + nanoid: "npm:^3.3.6" + picocolors: "npm:^1.0.0" + source-map-js: "npm:^1.0.2" + checksum: 10c0/748b82e6e5fc34034dcf2ae88ea3d11fd09f69b6c50ecdd3b4a875cfc7cdca435c958b211e2cb52355422ab6fccb7d8f2f2923161d7a1b281029e4a913d59acf + languageName: node + linkType: hard + +"postcss@npm:^8.4.23, postcss@npm:^8.4.39": + version: 8.4.39 + resolution: "postcss@npm:8.4.39" + dependencies: + nanoid: "npm:^3.3.7" + picocolors: "npm:^1.0.1" + source-map-js: "npm:^1.2.0" + checksum: 10c0/16f5ac3c4e32ee76d1582b3c0dcf1a1fdb91334a45ad755eeb881ccc50318fb8d64047de4f1601ac96e30061df203f0f2e2edbdc0bfc49b9c57bc9fb9bedaea3 + languageName: node + linkType: hard + +"pretty-format@npm:^29.7.0": + version: 29.7.0 + resolution: "pretty-format@npm:29.7.0" + dependencies: + "@jest/schemas": "npm:^29.6.3" + ansi-styles: "npm:^5.0.0" + react-is: "npm:^18.0.0" + checksum: 10c0/edc5ff89f51916f036c62ed433506b55446ff739358de77207e63e88a28ca2894caac6e73dcb68166a606e51c8087d32d400473e6a9fdd2dbe743f46c9c0276f + languageName: node + linkType: hard + +"prisma@npm:^5.17.0": + version: 5.17.0 + resolution: "prisma@npm:5.17.0" + dependencies: + "@prisma/engines": "npm:5.17.0" + bin: + prisma: build/index.js + checksum: 10c0/30546a8576ffadf66d6f34cd833e25e21eec99847db92c4d88f6c9dbbc401abbd3f699f9e0f0dbcd9d5229ccba47c6aadb42ba6cd6e29afb7335689c7257c964 + languageName: node + linkType: hard + +"prismjs@npm:^1.27.0": + version: 1.29.0 + resolution: "prismjs@npm:1.29.0" + checksum: 10c0/d906c4c4d01b446db549b4f57f72d5d7e6ccaca04ecc670fb85cea4d4b1acc1283e945a9cbc3d81819084a699b382f970e02f9d1378e14af9808d366d9ed7ec6 + languageName: node + linkType: hard + +"prismjs@npm:~1.27.0": + version: 1.27.0 + resolution: "prismjs@npm:1.27.0" + checksum: 10c0/841cbf53e837a42df9155c5ce1be52c4a0a8967ac916b52a27d066181a3578186c634e52d06d0547fb62b65c486b99b95f826dd54966619f9721b884f486b498 + languageName: node + linkType: hard + +"proc-log@npm:^4.0.0, proc-log@npm:^4.1.0, proc-log@npm:^4.2.0": + version: 4.2.0 + resolution: "proc-log@npm:4.2.0" + checksum: 10c0/17db4757c2a5c44c1e545170e6c70a26f7de58feb985091fb1763f5081cab3d01b181fb2dd240c9f4a4255a1d9227d163d5771b7e69c9e49a561692db865efb9 + languageName: node + linkType: hard + +"process-nextick-args@npm:~2.0.0": + version: 2.0.1 + resolution: "process-nextick-args@npm:2.0.1" + checksum: 10c0/bec089239487833d46b59d80327a1605e1c5287eaad770a291add7f45fda1bb5e28b38e0e061add0a1d0ee0984788ce74fa394d345eed1c420cacf392c554367 + languageName: node + linkType: hard + +"proggy@npm:^2.0.0": + version: 2.0.0 + resolution: "proggy@npm:2.0.0" + checksum: 10c0/1bfc14fa95769e6dd7e91f9d3cae8feb61e6d833ed7210d87ee5413bfa068f4ee7468483da96b2f138c40a7e91a2307f5d5d2eb6de9761c21e266a34602e6a5f + languageName: node + linkType: hard + +"promise-all-reject-late@npm:^1.0.0": + version: 1.0.1 + resolution: "promise-all-reject-late@npm:1.0.1" + checksum: 10c0/f1af0c7b0067e84d64751148ee5bb6c3e84f4a4d1316d6fe56261e1d2637cf71b49894bcbd2c6daf7d45afb1bc99efc3749be277c3e0518b70d0c5a29d037011 + languageName: node + linkType: hard + +"promise-call-limit@npm:^3.0.1": + version: 3.0.1 + resolution: "promise-call-limit@npm:3.0.1" + checksum: 10c0/2bf66a7238b9986c9b1ae0b3575c1446485b85b4befd9ee359d8386d26050d053cb2aaa57e0fc5d91e230a77e29ad546640b3afe3eb86bcfc204aa0d330f49b4 + languageName: node + linkType: hard + +"promise-inflight@npm:^1.0.1": + version: 1.0.1 + resolution: "promise-inflight@npm:1.0.1" + checksum: 10c0/d179d148d98fbff3d815752fa9a08a87d3190551d1420f17c4467f628214db12235ae068d98cd001f024453676d8985af8f28f002345646c4ece4600a79620bc + languageName: node + linkType: hard + +"promise-retry@npm:^2.0.1": + version: 2.0.1 + resolution: "promise-retry@npm:2.0.1" + dependencies: + err-code: "npm:^2.0.2" + retry: "npm:^0.12.0" + checksum: 10c0/9c7045a1a2928094b5b9b15336dcd2a7b1c052f674550df63cc3f36cd44028e5080448175b6f6ca32b642de81150f5e7b1a98b728f15cb069f2dd60ac2616b96 + languageName: node + linkType: hard + +"promzard@npm:^1.0.0": + version: 1.0.2 + resolution: "promzard@npm:1.0.2" + dependencies: + read: "npm:^3.0.1" + checksum: 10c0/d53c4ecb8b606b7e4bdeab14ac22c5f81a57463d29de1b8fe43bbc661106d9e4a79d07044bd3f69bde82c7ebacba7307db90a9699bc20482ce637bdea5fb8e4b + languageName: node + linkType: hard + +"prop-types@npm:^15.6.2, prop-types@npm:^15.8.1": + version: 15.8.1 + resolution: "prop-types@npm:15.8.1" + dependencies: + loose-envify: "npm:^1.4.0" + object-assign: "npm:^4.1.1" + react-is: "npm:^16.13.1" + checksum: 10c0/59ece7ca2fb9838031d73a48d4becb9a7cc1ed10e610517c7d8f19a1e02fa47f7c27d557d8a5702bec3cfeccddc853579832b43f449e54635803f277b1c78077 + languageName: node + linkType: hard + +"property-information@npm:^5.0.0": + version: 5.6.0 + resolution: "property-information@npm:5.6.0" + dependencies: + xtend: "npm:^4.0.0" + checksum: 10c0/d54b77c31dc13bb6819559080b2c67d37d94be7dc271f404f139a16a57aa96fcc0b3ad806d4a5baef9e031744853e4afe3df2e37275aacb1f78079bbb652c5af + languageName: node + linkType: hard + +"prosemirror-changeset@npm:^2.2.1": + version: 2.2.1 + resolution: "prosemirror-changeset@npm:2.2.1" + dependencies: + prosemirror-transform: "npm:^1.0.0" + checksum: 10c0/0a16092149ca0021a44ab5eb6a0c6dc425525507bde9e3772fbd3944b6cfa601d38492198b5410f2637694aedf7478d121b0744f430a8b7a5eb1d0fb9fbd49d1 + languageName: node + linkType: hard + +"prosemirror-collab@npm:^1.3.1": + version: 1.3.1 + resolution: "prosemirror-collab@npm:1.3.1" + dependencies: + prosemirror-state: "npm:^1.0.0" + checksum: 10c0/5d7553c136929cfd847b8781be599561d0f21e78fae80d930eb5f1d4d644307bc779cdfaeae86dd31a8be8f562c28dee19f1a06a2900e9b591b02957151fe90c + languageName: node + linkType: hard + +"prosemirror-commands@npm:^1.0.0, prosemirror-commands@npm:^1.5.2": + version: 1.5.2 + resolution: "prosemirror-commands@npm:1.5.2" + dependencies: + prosemirror-model: "npm:^1.0.0" + prosemirror-state: "npm:^1.0.0" + prosemirror-transform: "npm:^1.0.0" + checksum: 10c0/9ff0b525d4bc654ecd41a27f11d8aff52f719ea9a7da2587d9632cfc00bcac46ecc3be628623d1a768e3aa7c7ed2fe291326bb7d63b0a5c0814e53b0a6af5b35 + languageName: node + linkType: hard + +"prosemirror-dropcursor@npm:^1.8.1": + version: 1.8.1 + resolution: "prosemirror-dropcursor@npm:1.8.1" + dependencies: + prosemirror-state: "npm:^1.0.0" + prosemirror-transform: "npm:^1.1.0" + prosemirror-view: "npm:^1.1.0" + checksum: 10c0/2948cac48efb32757b212bd7cc5a50697ea6c3f6e4cd7752a2696c56b758fa0a16a5a6e288174a649544a0260a6b70d29e0fcb8839a05926c1c3a02f8de03aed + languageName: node + linkType: hard + +"prosemirror-gapcursor@npm:^1.3.2": + version: 1.3.2 + resolution: "prosemirror-gapcursor@npm:1.3.2" + dependencies: + prosemirror-keymap: "npm:^1.0.0" + prosemirror-model: "npm:^1.0.0" + prosemirror-state: "npm:^1.0.0" + prosemirror-view: "npm:^1.0.0" + checksum: 10c0/2e3f6f17ecd02392dd567019a5c69798cc7c2f09c950b59882ae37159f92e94a193440722715052ca92ea9914b85b8b0bcf693ea9daeb5271914b846acae1f91 + languageName: node + linkType: hard + +"prosemirror-history@npm:^1.0.0, prosemirror-history@npm:^1.4.1": + version: 1.4.1 + resolution: "prosemirror-history@npm:1.4.1" + dependencies: + prosemirror-state: "npm:^1.2.2" + prosemirror-transform: "npm:^1.0.0" + prosemirror-view: "npm:^1.31.0" + rope-sequence: "npm:^1.3.0" + checksum: 10c0/fd2dfae5fb956a8710bb1a4131e9b6d8b92e846bf88fa643bc59ba595c8a835f6695574d5e33bcea9a6e7fbf2eafc7c1b1003abf11326e8571e196cd0f16dcd8 + languageName: node + linkType: hard + +"prosemirror-inputrules@npm:^1.4.0": + version: 1.4.0 + resolution: "prosemirror-inputrules@npm:1.4.0" + dependencies: + prosemirror-state: "npm:^1.0.0" + prosemirror-transform: "npm:^1.0.0" + checksum: 10c0/8ec72b6c2982bbd9fd378e51d67c6424119d081a4dcdeff430ab58055596cf67b691a890f46f135746f4de9bc6a6afb6ef1c0596df13bd633997e32ba0a25ddf + languageName: node + linkType: hard + +"prosemirror-keymap@npm:^1.0.0, prosemirror-keymap@npm:^1.1.2, prosemirror-keymap@npm:^1.2.2": + version: 1.2.2 + resolution: "prosemirror-keymap@npm:1.2.2" + dependencies: + prosemirror-state: "npm:^1.0.0" + w3c-keyname: "npm:^2.2.0" + checksum: 10c0/7aa28c731e00962c90c91361a3c9f7000f960870a1300f7477da8afa8fd1b9cce0b3b7ca483aaa5832fd0bf88b5ff081defc184592997b08980b9ab67eeddcb7 + languageName: node + linkType: hard + +"prosemirror-markdown@npm:^1.13.0": + version: 1.13.0 + resolution: "prosemirror-markdown@npm:1.13.0" + dependencies: + markdown-it: "npm:^14.0.0" + prosemirror-model: "npm:^1.20.0" + checksum: 10c0/3f4c7603da4795db8233a78ff2769f901d368fa82049fb651dc9e7db9ed7e057cdd704f248f37792b0b2814df6317714a960e4418ffcd1078c02f2cd08c8f906 + languageName: node + linkType: hard + +"prosemirror-menu@npm:^1.2.4": + version: 1.2.4 + resolution: "prosemirror-menu@npm:1.2.4" + dependencies: + crelt: "npm:^1.0.0" + prosemirror-commands: "npm:^1.0.0" + prosemirror-history: "npm:^1.0.0" + prosemirror-state: "npm:^1.0.0" + checksum: 10c0/7c12e618f99c0ca4de5b117a40c6df4b321607e7b4395e181de8cfcd5cb803784363c1bb4ef8603f6e2f7f6fc7859cb165bd33d43d6c1b211b00d868144f8361 + languageName: node + linkType: hard + +"prosemirror-model@npm:^1.0.0, prosemirror-model@npm:^1.19.0, prosemirror-model@npm:^1.20.0, prosemirror-model@npm:^1.21.0, prosemirror-model@npm:^1.22.1, prosemirror-model@npm:^1.8.1": + version: 1.22.2 + resolution: "prosemirror-model@npm:1.22.2" + dependencies: + orderedmap: "npm:^2.0.0" + checksum: 10c0/60935c18b779d93c64bd6e4a74f257efab3d539500c635cebdcec27005091ec9d297b87d7668645098db7a269f0e50184abae07ebfabf7bc73e747342c488380 + languageName: node + linkType: hard + +"prosemirror-schema-basic@npm:^1.2.3": + version: 1.2.3 + resolution: "prosemirror-schema-basic@npm:1.2.3" + dependencies: + prosemirror-model: "npm:^1.19.0" + checksum: 10c0/99bac902ccf046e2dd165a3c124c6458be8041f3e4322f64fd9d37e6ee164e0d4284cc17691734665a62431f35045798cf417b7d174aa1af6dc2a48dc51468ac + languageName: node + linkType: hard + +"prosemirror-schema-list@npm:^1.4.1": + version: 1.4.1 + resolution: "prosemirror-schema-list@npm:1.4.1" + dependencies: + prosemirror-model: "npm:^1.0.0" + prosemirror-state: "npm:^1.0.0" + prosemirror-transform: "npm:^1.7.3" + checksum: 10c0/61c664bea2343b13db47d4f5d86dafb453f0102f7b85fa8ad0432e9c7ef5d14134ceb275ff3419cf2be85d4a7fb9e6974945f2b4b652d0cf3a3aca586f5e0838 + languageName: node + linkType: hard + +"prosemirror-state@npm:^1.0.0, prosemirror-state@npm:^1.2.2, prosemirror-state@npm:^1.3.1, prosemirror-state@npm:^1.4.3": + version: 1.4.3 + resolution: "prosemirror-state@npm:1.4.3" + dependencies: + prosemirror-model: "npm:^1.0.0" + prosemirror-transform: "npm:^1.0.0" + prosemirror-view: "npm:^1.27.0" + checksum: 10c0/e34dc9b1a6b23c23265569b2c246aaef4a61353a5fd33e933b62528917603382271d9f7d5212094e8928dee9bb4827e25a583104d43745e6ab3b8cbde12170f5 + languageName: node + linkType: hard + +"prosemirror-tables@npm:^1.3.7": + version: 1.4.0 + resolution: "prosemirror-tables@npm:1.4.0" + dependencies: + prosemirror-keymap: "npm:^1.1.2" + prosemirror-model: "npm:^1.8.1" + prosemirror-state: "npm:^1.3.1" + prosemirror-transform: "npm:^1.2.1" + prosemirror-view: "npm:^1.13.3" + checksum: 10c0/11dcfea569cfba42d11989cc2fcf7d873529e4d557d499440ea72fda1011e2fbc5f4c1b2f79cd0c0e6e96c206e396c8ef419b4aadc8ea80d21bfdcbfa5d8bab2 + languageName: node + linkType: hard + +"prosemirror-trailing-node@npm:^2.0.8": + version: 2.0.9 + resolution: "prosemirror-trailing-node@npm:2.0.9" + dependencies: + "@remirror/core-constants": "npm:^2.0.2" + escape-string-regexp: "npm:^4.0.0" + peerDependencies: + prosemirror-model: ^1.22.1 + prosemirror-state: ^1.4.2 + prosemirror-view: ^1.33.8 + checksum: 10c0/1eb23c82e47dc0659a666cdfaf13b90bd086a5cc21f67ad52516983b99f8710487bf4bf4aaa7e4cebe9096f380bf76aea966ff95da95d5ffc67cec55d98ae834 + languageName: node + linkType: hard + +"prosemirror-transform@npm:^1.0.0, prosemirror-transform@npm:^1.1.0, prosemirror-transform@npm:^1.2.1, prosemirror-transform@npm:^1.7.3, prosemirror-transform@npm:^1.9.0": + version: 1.9.0 + resolution: "prosemirror-transform@npm:1.9.0" + dependencies: + prosemirror-model: "npm:^1.21.0" + checksum: 10c0/8832d825a9d38fd116f5c3fbc9708f3aa627f88980efbc1923524da7673f5bd9d7ed0032762f5be223d990176a759b2ae0a5f3ba67bffd596c028ddbc0d53b93 + languageName: node + linkType: hard + +"prosemirror-view@npm:^1.0.0, prosemirror-view@npm:^1.1.0, prosemirror-view@npm:^1.13.3, prosemirror-view@npm:^1.27.0, prosemirror-view@npm:^1.31.0, prosemirror-view@npm:^1.33.8": + version: 1.33.9 + resolution: "prosemirror-view@npm:1.33.9" + dependencies: + prosemirror-model: "npm:^1.20.0" + prosemirror-state: "npm:^1.0.0" + prosemirror-transform: "npm:^1.1.0" + checksum: 10c0/69e17ee613fe9d69fcb2201664f9878aefda80aa662ead9184fa30b3e14691e7a14952d325decc2d3877417f13fe8dbfa1291d486b34701407962b061100a195 + languageName: node + linkType: hard + +"proto-list@npm:~1.2.1": + version: 1.2.4 + resolution: "proto-list@npm:1.2.4" + checksum: 10c0/b9179f99394ec8a68b8afc817690185f3b03933f7b46ce2e22c1930dc84b60d09f5ad222beab4e59e58c6c039c7f7fcf620397235ef441a356f31f9744010e12 + languageName: node + linkType: hard + +"protocols@npm:^2.0.0, protocols@npm:^2.0.1": + version: 2.0.1 + resolution: "protocols@npm:2.0.1" + checksum: 10c0/016cc58a596e401004a028a2f7005e3444bf89ee8f606409c411719374d1e8bba0464fc142a065cce0d19f41669b2f7ffe25a8bde4f16ce3b6eb01fabc51f2e7 + languageName: node + linkType: hard + +"proxy-addr@npm:~2.0.7": + version: 2.0.7 + resolution: "proxy-addr@npm:2.0.7" + dependencies: + forwarded: "npm:0.2.0" + ipaddr.js: "npm:1.9.1" + checksum: 10c0/c3eed999781a35f7fd935f398b6d8920b6fb00bbc14287bc6de78128ccc1a02c89b95b56742bf7cf0362cc333c61d138532049c7dedc7a328ef13343eff81210 + languageName: node + linkType: hard + +"proxy-from-env@npm:^1.1.0": + version: 1.1.0 + resolution: "proxy-from-env@npm:1.1.0" + checksum: 10c0/fe7dd8b1bdbbbea18d1459107729c3e4a2243ca870d26d34c2c1bcd3e4425b7bcc5112362df2d93cc7fb9746f6142b5e272fd1cc5c86ddf8580175186f6ad42b + languageName: node + linkType: hard + +"punycode.js@npm:^2.3.1": + version: 2.3.1 + resolution: "punycode.js@npm:2.3.1" + checksum: 10c0/1d12c1c0e06127fa5db56bd7fdf698daf9a78104456a6b67326877afc21feaa821257b171539caedd2f0524027fa38e67b13dd094159c8d70b6d26d2bea4dfdb + languageName: node + linkType: hard + +"qs@npm:6.11.0": + version: 6.11.0 + resolution: "qs@npm:6.11.0" + dependencies: + side-channel: "npm:^1.0.4" + checksum: 10c0/4e4875e4d7c7c31c233d07a448e7e4650f456178b9dd3766b7cfa13158fdb24ecb8c4f059fa91e820dc6ab9f2d243721d071c9c0378892dcdad86e9e9a27c68f + languageName: node + linkType: hard + +"queue-microtask@npm:^1.2.2": + version: 1.2.3 + resolution: "queue-microtask@npm:1.2.3" + checksum: 10c0/900a93d3cdae3acd7d16f642c29a642aea32c2026446151f0778c62ac089d4b8e6c986811076e1ae180a694cedf077d453a11b58ff0a865629a4f82ab558e102 + languageName: node + linkType: hard + +"quick-lru@npm:^4.0.1": + version: 4.0.1 + resolution: "quick-lru@npm:4.0.1" + checksum: 10c0/f9b1596fa7595a35c2f9d913ac312fede13d37dc8a747a51557ab36e11ce113bbe88ef4c0154968845559a7709cb6a7e7cbe75f7972182451cd45e7f057a334d + languageName: node + linkType: hard + +"range-parser@npm:~1.2.1": + version: 1.2.1 + resolution: "range-parser@npm:1.2.1" + checksum: 10c0/96c032ac2475c8027b7a4e9fe22dc0dfe0f6d90b85e496e0f016fbdb99d6d066de0112e680805075bd989905e2123b3b3d002765149294dce0c1f7f01fcc2ea0 + languageName: node + linkType: hard + +"raw-body@npm:2.5.2": + version: 2.5.2 + resolution: "raw-body@npm:2.5.2" + dependencies: + bytes: "npm:3.1.2" + http-errors: "npm:2.0.0" + iconv-lite: "npm:0.4.24" + unpipe: "npm:1.0.0" + checksum: 10c0/b201c4b66049369a60e766318caff5cb3cc5a900efd89bdac431463822d976ad0670912c931fdbdcf5543207daf6f6833bca57aa116e1661d2ea91e12ca692c4 + languageName: node + linkType: hard + +"react-dom@npm:18.3.1": + version: 18.3.1 + resolution: "react-dom@npm:18.3.1" + dependencies: + loose-envify: "npm:^1.1.0" + scheduler: "npm:^0.23.2" + peerDependencies: + react: ^18.3.1 + checksum: 10c0/a752496c1941f958f2e8ac56239172296fcddce1365ce45222d04a1947e0cc5547df3e8447f855a81d6d39f008d7c32eab43db3712077f09e3f67c4874973e85 + languageName: node + linkType: hard + +"react-hook-form@npm:^7.52.1": + version: 7.52.1 + resolution: "react-hook-form@npm:7.52.1" + peerDependencies: + react: ^16.8.0 || ^17 || ^18 || ^19 + checksum: 10c0/68f21daa66f3c2b87c83f2f9b1dbd160a856b24e9315263af24fee8737b62336dc7cc43e09c581ff957ed032a3ac2355f69b1c1d32d89893a6e1f601112eacfd + languageName: node + linkType: hard + +"react-is@npm:^16.10.2, react-is@npm:^16.13.1": + version: 16.13.1 + resolution: "react-is@npm:16.13.1" + checksum: 10c0/33977da7a5f1a287936a0c85639fec6ca74f4f15ef1e59a6bc20338fc73dc69555381e211f7a3529b8150a1f71e4225525b41b60b52965bda53ce7d47377ada1 + languageName: node + linkType: hard + +"react-is@npm:^18.0.0": + version: 18.3.1 + resolution: "react-is@npm:18.3.1" + checksum: 10c0/f2f1e60010c683479e74c63f96b09fb41603527cd131a9959e2aee1e5a8b0caf270b365e5ca77d4a6b18aae659b60a86150bb3979073528877029b35aecd2072 + languageName: node + linkType: hard + +"react-smooth@npm:^4.0.0": + version: 4.0.1 + resolution: "react-smooth@npm:4.0.1" + dependencies: + fast-equals: "npm:^5.0.1" + prop-types: "npm:^15.8.1" + react-transition-group: "npm:^4.4.5" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 10c0/5c19a2c147798c3de1329d442b1a371139c01113cc108c38c201b63502c329f943ede505c44089d26a6563eaa72a67b845d538d956f34a389b37fd3961308834 + languageName: node + linkType: hard + +"react-syntax-highlighter@npm:^15.5.0": + version: 15.5.0 + resolution: "react-syntax-highlighter@npm:15.5.0" + dependencies: + "@babel/runtime": "npm:^7.3.1" + highlight.js: "npm:^10.4.1" + lowlight: "npm:^1.17.0" + prismjs: "npm:^1.27.0" + refractor: "npm:^3.6.0" + peerDependencies: + react: ">= 0.14.0" + checksum: 10c0/2bf57a1ea151f688efc7eba355677577c9bb55f05f9df7ef86627aae42f63f505486cddf3f4a628aecc51ec75e89beb9533201570d03201c4bf7d69d61d2545d + languageName: node + linkType: hard + +"react-transition-group@npm:^4.4.5": + version: 4.4.5 + resolution: "react-transition-group@npm:4.4.5" + dependencies: + "@babel/runtime": "npm:^7.5.5" + dom-helpers: "npm:^5.0.1" + loose-envify: "npm:^1.4.0" + prop-types: "npm:^15.6.2" + peerDependencies: + react: ">=16.6.0" + react-dom: ">=16.6.0" + checksum: 10c0/2ba754ba748faefa15f87c96dfa700d5525054a0141de8c75763aae6734af0740e77e11261a1e8f4ffc08fd9ab78510122e05c21c2d79066c38bb6861a886c82 + languageName: node + linkType: hard + +"react@npm:18.3.1": + version: 18.3.1 + resolution: "react@npm:18.3.1" + dependencies: + loose-envify: "npm:^1.1.0" + checksum: 10c0/283e8c5efcf37802c9d1ce767f302dd569dd97a70d9bb8c7be79a789b9902451e0d16334b05d73299b20f048cbc3c7d288bbbde10b701fa194e2089c237dbea3 + languageName: node + linkType: hard + +"read-cache@npm:^1.0.0": + version: 1.0.0 + resolution: "read-cache@npm:1.0.0" + dependencies: + pify: "npm:^2.3.0" + checksum: 10c0/90cb2750213c7dd7c80cb420654344a311fdec12944e81eb912cd82f1bc92aea21885fa6ce442e3336d9fccd663b8a7a19c46d9698e6ca55620848ab932da814 + languageName: node + linkType: hard + +"read-cmd-shim@npm:4.0.0, read-cmd-shim@npm:^4.0.0": + version: 4.0.0 + resolution: "read-cmd-shim@npm:4.0.0" + checksum: 10c0/e62db17ec9708f1e7c6a31f0a46d43df2069d85cf0df3b9d1d99e5ed36e29b1e8b2f8a427fd8bbb9bc40829788df1471794f9b01057e4b95ed062806e4df5ba9 + languageName: node + linkType: hard + +"read-package-json-fast@npm:^3.0.0, read-package-json-fast@npm:^3.0.2": + version: 3.0.2 + resolution: "read-package-json-fast@npm:3.0.2" + dependencies: + json-parse-even-better-errors: "npm:^3.0.0" + npm-normalize-package-bin: "npm:^3.0.0" + checksum: 10c0/37787e075f0260a92be0428687d9020eecad7ece3bda37461c2219e50d1ec183ab6ba1d9ada193691435dfe119a42c8a5b5b5463f08c8ddbc3d330800b265318 + languageName: node + linkType: hard + +"read-pkg-up@npm:^3.0.0": + version: 3.0.0 + resolution: "read-pkg-up@npm:3.0.0" + dependencies: + find-up: "npm:^2.0.0" + read-pkg: "npm:^3.0.0" + checksum: 10c0/2cd0a180260b0d235990e6e9c8c2330a03882d36bc2eba8930e437ef23ee52a68a894e7e1ccb1c33f03bcceb270a861ee5f7eac686f238857755e2cddfb48ffd + languageName: node + linkType: hard + +"read-pkg-up@npm:^7.0.1": + version: 7.0.1 + resolution: "read-pkg-up@npm:7.0.1" + dependencies: + find-up: "npm:^4.1.0" + read-pkg: "npm:^5.2.0" + type-fest: "npm:^0.8.1" + checksum: 10c0/82b3ac9fd7c6ca1bdc1d7253eb1091a98ff3d195ee0a45386582ce3e69f90266163c34121e6a0a02f1630073a6c0585f7880b3865efcae9c452fa667f02ca385 + languageName: node + linkType: hard + +"read-pkg@npm:^3.0.0": + version: 3.0.0 + resolution: "read-pkg@npm:3.0.0" + dependencies: + load-json-file: "npm:^4.0.0" + normalize-package-data: "npm:^2.3.2" + path-type: "npm:^3.0.0" + checksum: 10c0/65acf2df89fbcd506b48b7ced56a255ba00adf7ecaa2db759c86cc58212f6fd80f1f0b7a85c848551a5d0685232e9b64f45c1fd5b48d85df2761a160767eeb93 + languageName: node + linkType: hard + +"read-pkg@npm:^5.2.0": + version: 5.2.0 + resolution: "read-pkg@npm:5.2.0" + dependencies: + "@types/normalize-package-data": "npm:^2.4.0" + normalize-package-data: "npm:^2.5.0" + parse-json: "npm:^5.0.0" + type-fest: "npm:^0.6.0" + checksum: 10c0/b51a17d4b51418e777029e3a7694c9bd6c578a5ab99db544764a0b0f2c7c0f58f8a6bc101f86a6fceb8ba6d237d67c89acf6170f6b98695d0420ddc86cf109fb + languageName: node + linkType: hard + +"read@npm:^3.0.1": + version: 3.0.1 + resolution: "read@npm:3.0.1" + dependencies: + mute-stream: "npm:^1.0.0" + checksum: 10c0/af524994ff7cf94aa3ebd268feac509da44e58be7ed2a02775b5ee6a7d157b93b919e8c5ead91333f86a21fbb487dc442760bc86354c18b84d334b8cec33723a + languageName: node + linkType: hard + +"readable-stream@npm:^2.2.2, readable-stream@npm:~2.3.6": + version: 2.3.8 + resolution: "readable-stream@npm:2.3.8" + dependencies: + core-util-is: "npm:~1.0.0" + inherits: "npm:~2.0.3" + isarray: "npm:~1.0.0" + process-nextick-args: "npm:~2.0.0" + safe-buffer: "npm:~5.1.1" + string_decoder: "npm:~1.1.1" + util-deprecate: "npm:~1.0.1" + checksum: 10c0/7efdb01f3853bc35ac62ea25493567bf588773213f5f4a79f9c365e1ad13bab845ac0dae7bc946270dc40c3929483228415e92a3fc600cc7e4548992f41ee3fa + languageName: node + linkType: hard + +"readable-stream@npm:^3.0.0, readable-stream@npm:^3.0.2, readable-stream@npm:^3.1.1, readable-stream@npm:^3.4.0, readable-stream@npm:^3.6.0": + version: 3.6.2 + resolution: "readable-stream@npm:3.6.2" + dependencies: + inherits: "npm:^2.0.3" + string_decoder: "npm:^1.1.1" + util-deprecate: "npm:^1.0.1" + checksum: 10c0/e37be5c79c376fdd088a45fa31ea2e423e5d48854be7a22a58869b4e84d25047b193f6acb54f1012331e1bcd667ffb569c01b99d36b0bd59658fb33f513511b7 + languageName: node + linkType: hard + +"readdirp@npm:~3.6.0": + version: 3.6.0 + resolution: "readdirp@npm:3.6.0" + dependencies: + picomatch: "npm:^2.2.1" + checksum: 10c0/6fa848cf63d1b82ab4e985f4cf72bd55b7dcfd8e0a376905804e48c3634b7e749170940ba77b32804d5fe93b3cc521aa95a8d7e7d725f830da6d93f3669ce66b + languageName: node + linkType: hard + +"recharts-scale@npm:^0.4.4": + version: 0.4.5 + resolution: "recharts-scale@npm:0.4.5" + dependencies: + decimal.js-light: "npm:^2.4.1" + checksum: 10c0/64ce1fc4ebe62001787bf4dc4cbb779452d33831619309c71c50277c58e8968ffe98941562d9d0d5ffdb02588ebd62f4fe6548fa826110fd458db9c3cc6dadc1 + languageName: node + linkType: hard + +"recharts@npm:^2.12.7": + version: 2.12.7 + resolution: "recharts@npm:2.12.7" + dependencies: + clsx: "npm:^2.0.0" + eventemitter3: "npm:^4.0.1" + lodash: "npm:^4.17.21" + react-is: "npm:^16.10.2" + react-smooth: "npm:^4.0.0" + recharts-scale: "npm:^0.4.4" + tiny-invariant: "npm:^1.3.1" + victory-vendor: "npm:^36.6.8" + peerDependencies: + react: ^16.0.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 + checksum: 10c0/2522d841a1f4e4c0a37046ddb61fa958ac37a66df63dcd4c6cb9113e3f7a71892d74e44494a55bc40faa0afd74d9cf58fec3d2ce53a8ddf997e75367bdd033fc + languageName: node + linkType: hard + +"redent@npm:^3.0.0": + version: 3.0.0 + resolution: "redent@npm:3.0.0" + dependencies: + indent-string: "npm:^4.0.0" + strip-indent: "npm:^3.0.0" + checksum: 10c0/d64a6b5c0b50eb3ddce3ab770f866658a2b9998c678f797919ceb1b586bab9259b311407280bd80b804e2a7c7539b19238ae6a2a20c843f1a7fcff21d48c2eae + languageName: node + linkType: hard + +"redis-errors@npm:^1.0.0, redis-errors@npm:^1.2.0": + version: 1.2.0 + resolution: "redis-errors@npm:1.2.0" + checksum: 10c0/5b316736e9f532d91a35bff631335137a4f974927bb2fb42bf8c2f18879173a211787db8ac4c3fde8f75ed6233eb0888e55d52510b5620e30d69d7d719c8b8a7 + languageName: node + linkType: hard + +"redis-parser@npm:^3.0.0": + version: 3.0.0 + resolution: "redis-parser@npm:3.0.0" + dependencies: + redis-errors: "npm:^1.0.0" + checksum: 10c0/ee16ac4c7b2a60b1f42a2cdaee22b005bd4453eb2d0588b8a4939718997ae269da717434da5d570fe0b05030466eeb3f902a58cf2e8e1ca058bf6c9c596f632f + languageName: node + linkType: hard + +"reflect-metadata@npm:^0.1.13": + version: 0.1.14 + resolution: "reflect-metadata@npm:0.1.14" + checksum: 10c0/3a6190c7f6cb224f26a012d11f9e329360c01c1945e2cbefea23976a8bacf9db6b794aeb5bf18adcb673c448a234fbc06fc41853c00a6c206b30f0777ecf019e + languageName: node + linkType: hard + +"refractor@npm:^3.6.0": + version: 3.6.0 + resolution: "refractor@npm:3.6.0" + dependencies: + hastscript: "npm:^6.0.0" + parse-entities: "npm:^2.0.0" + prismjs: "npm:~1.27.0" + checksum: 10c0/63ab62393c8c2fd7108c2ea1eff721c0ad2a1a6eee60fdd1b47f4bb25cf298667dc97d041405b3e718b0817da12b37a86ed07ebee5bd2ca6405611f1bae456db + languageName: node + linkType: hard + +"regenerate-unicode-properties@npm:^10.1.0": + version: 10.1.1 + resolution: "regenerate-unicode-properties@npm:10.1.1" + dependencies: + regenerate: "npm:^1.4.2" + checksum: 10c0/89adb5ee5ba081380c78f9057c02e156a8181969f6fcca72451efc45612e0c3df767b4333f8d8479c274d9c6fe52ec4854f0d8a22ef95dccbe87da8e5f2ac77d + languageName: node + linkType: hard + +"regenerate@npm:^1.4.2": + version: 1.4.2 + resolution: "regenerate@npm:1.4.2" + checksum: 10c0/f73c9eba5d398c818edc71d1c6979eaa05af7a808682749dd079f8df2a6d91a9b913db216c2c9b03e0a8ba2bba8701244a93f45211afbff691c32c7b275db1b8 + languageName: node + linkType: hard + +"regenerator-runtime@npm:^0.14.0": + version: 0.14.1 + resolution: "regenerator-runtime@npm:0.14.1" + checksum: 10c0/1b16eb2c4bceb1665c89de70dcb64126a22bc8eb958feef3cd68fe11ac6d2a4899b5cd1b80b0774c7c03591dc57d16631a7f69d2daa2ec98100e2f29f7ec4cc4 + languageName: node + linkType: hard + +"regenerator-transform@npm:^0.15.2": + version: 0.15.2 + resolution: "regenerator-transform@npm:0.15.2" + dependencies: + "@babel/runtime": "npm:^7.8.4" + checksum: 10c0/7cfe6931ec793269701994a93bab89c0cc95379191fad866270a7fea2adfec67ea62bb5b374db77058b60ba4509319d9b608664d0d288bd9989ca8dbd08fae90 + languageName: node + linkType: hard + +"regexpu-core@npm:^5.3.1": + version: 5.3.2 + resolution: "regexpu-core@npm:5.3.2" + dependencies: + "@babel/regjsgen": "npm:^0.8.0" + regenerate: "npm:^1.4.2" + regenerate-unicode-properties: "npm:^10.1.0" + regjsparser: "npm:^0.9.1" + unicode-match-property-ecmascript: "npm:^2.0.0" + unicode-match-property-value-ecmascript: "npm:^2.1.0" + checksum: 10c0/7945d5ab10c8bbed3ca383d4274687ea825aee4ab93a9c51c6e31e1365edd5ea807f6908f800ba017b66c462944ba68011164e7055207747ab651f8111ef3770 + languageName: node + linkType: hard + +"regjsparser@npm:^0.9.1": + version: 0.9.1 + resolution: "regjsparser@npm:0.9.1" + dependencies: + jsesc: "npm:~0.5.0" + bin: + regjsparser: bin/parser + checksum: 10c0/fe44fcf19a99fe4f92809b0b6179530e5ef313ff7f87df143b08ce9a2eb3c4b6189b43735d645be6e8f4033bfb015ed1ca54f0583bc7561bed53fd379feb8225 + languageName: node + linkType: hard + +"relateurl@npm:^0.2.7": + version: 0.2.7 + resolution: "relateurl@npm:0.2.7" + checksum: 10c0/c248b4e3b32474f116a804b537fa6343d731b80056fb506dffd91e737eef4cac6be47a65aae39b522b0db9d0b1011d1a12e288d82a109ecd94a5299d82f6573a + languageName: node + linkType: hard + +"require-directory@npm:^2.1.1": + version: 2.1.1 + resolution: "require-directory@npm:2.1.1" + checksum: 10c0/83aa76a7bc1531f68d92c75a2ca2f54f1b01463cb566cf3fbc787d0de8be30c9dbc211d1d46be3497dac5785fe296f2dd11d531945ac29730643357978966e99 + languageName: node + linkType: hard + +"resolve-cwd@npm:^3.0.0": + version: 3.0.0 + resolution: "resolve-cwd@npm:3.0.0" + dependencies: + resolve-from: "npm:^5.0.0" + checksum: 10c0/e608a3ebd15356264653c32d7ecbc8fd702f94c6703ea4ac2fb81d9c359180cba0ae2e6b71faa446631ed6145454d5a56b227efc33a2d40638ac13f8beb20ee4 + languageName: node + linkType: hard + +"resolve-from@npm:5.0.0, resolve-from@npm:^5.0.0": + version: 5.0.0 + resolution: "resolve-from@npm:5.0.0" + checksum: 10c0/b21cb7f1fb746de8107b9febab60095187781137fd803e6a59a76d421444b1531b641bba5857f5dc011974d8a5c635d61cec49e6bd3b7fc20e01f0fafc4efbf2 + languageName: node + linkType: hard + +"resolve-from@npm:^4.0.0": + version: 4.0.0 + resolution: "resolve-from@npm:4.0.0" + checksum: 10c0/8408eec31a3112ef96e3746c37be7d64020cda07c03a920f5024e77290a218ea758b26ca9529fd7b1ad283947f34b2291c1c0f6aa0ed34acfdda9c6014c8d190 + languageName: node + linkType: hard + +"resolve@npm:^1.0.0, resolve@npm:^1.1.7, resolve@npm:^1.10.0, resolve@npm:^1.14.2, resolve@npm:^1.22.2": + version: 1.22.8 + resolution: "resolve@npm:1.22.8" + dependencies: + is-core-module: "npm:^2.13.0" + path-parse: "npm:^1.0.7" + supports-preserve-symlinks-flag: "npm:^1.0.0" + bin: + resolve: bin/resolve + checksum: 10c0/07e179f4375e1fd072cfb72ad66d78547f86e6196c4014b31cb0b8bb1db5f7ca871f922d08da0fbc05b94e9fd42206f819648fa3b5b873ebbc8e1dc68fec433a + languageName: node + linkType: hard + +"resolve@patch:resolve@npm%3A^1.0.0#optional!builtin, resolve@patch:resolve@npm%3A^1.1.7#optional!builtin, resolve@patch:resolve@npm%3A^1.10.0#optional!builtin, resolve@patch:resolve@npm%3A^1.14.2#optional!builtin, resolve@patch:resolve@npm%3A^1.22.2#optional!builtin": + version: 1.22.8 + resolution: "resolve@patch:resolve@npm%3A1.22.8#optional!builtin::version=1.22.8&hash=c3c19d" + dependencies: + is-core-module: "npm:^2.13.0" + path-parse: "npm:^1.0.7" + supports-preserve-symlinks-flag: "npm:^1.0.0" + bin: + resolve: bin/resolve + checksum: 10c0/0446f024439cd2e50c6c8fa8ba77eaa8370b4180f401a96abf3d1ebc770ac51c1955e12764cde449fde3fff480a61f84388e3505ecdbab778f4bef5f8212c729 + languageName: node + linkType: hard + +"restore-cursor@npm:^3.1.0": + version: 3.1.0 + resolution: "restore-cursor@npm:3.1.0" + dependencies: + onetime: "npm:^5.1.0" + signal-exit: "npm:^3.0.2" + checksum: 10c0/8051a371d6aa67ff21625fa94e2357bd81ffdc96267f3fb0fc4aaf4534028343836548ef34c240ffa8c25b280ca35eb36be00b3cb2133fa4f51896d7e73c6b4f + languageName: node + linkType: hard + +"retry@npm:^0.12.0": + version: 0.12.0 + resolution: "retry@npm:0.12.0" + checksum: 10c0/59933e8501727ba13ad73ef4a04d5280b3717fd650408460c987392efe9d7be2040778ed8ebe933c5cbd63da3dcc37919c141ef8af0a54a6e4fca5a2af177bfe + languageName: node + linkType: hard + +"reusify@npm:^1.0.4": + version: 1.0.4 + resolution: "reusify@npm:1.0.4" + checksum: 10c0/c19ef26e4e188f408922c46f7ff480d38e8dfc55d448310dfb518736b23ed2c4f547fb64a6ed5bdba92cd7e7ddc889d36ff78f794816d5e71498d645ef476107 + languageName: node + linkType: hard + +"rimraf@npm:^2.6.1": + version: 2.7.1 + resolution: "rimraf@npm:2.7.1" + dependencies: + glob: "npm:^7.1.3" + bin: + rimraf: ./bin.js + checksum: 10c0/4eef73d406c6940927479a3a9dee551e14a54faf54b31ef861250ac815172bade86cc6f7d64a4dc5e98b65e4b18a2e1c9ff3b68d296be0c748413f092bb0dd40 + languageName: node + linkType: hard + +"rimraf@npm:^3.0.2": + version: 3.0.2 + resolution: "rimraf@npm:3.0.2" + dependencies: + glob: "npm:^7.1.3" + bin: + rimraf: bin.js + checksum: 10c0/9cb7757acb489bd83757ba1a274ab545eafd75598a9d817e0c3f8b164238dd90eba50d6b848bd4dcc5f3040912e882dc7ba71653e35af660d77b25c381d402e8 + languageName: node + linkType: hard + +"rimraf@npm:^4.4.1": + version: 4.4.1 + resolution: "rimraf@npm:4.4.1" + dependencies: + glob: "npm:^9.2.0" + bin: + rimraf: dist/cjs/src/bin.js + checksum: 10c0/8c5e142d26d8b222be9dc9a1a41ba48e95d8f374e813e66a8533e87c6180174fcb3f573b9b592eca12740ebf8b78526d136acd971d4a790763d6f2232c34fa24 + languageName: node + linkType: hard + +"rimraf@npm:^5.0.9": + version: 5.0.9 + resolution: "rimraf@npm:5.0.9" + dependencies: + glob: "npm:^10.3.7" + bin: + rimraf: dist/esm/bin.mjs + checksum: 10c0/87374682492b9e64de9c6fcbf2c8f209c7a2cd0e9749b3732eef8a62c6f859a9ed996d46f662d9ad5dd38c2c469f8e88de56b6c509026070ee3f06369cac1bc8 + languageName: node + linkType: hard + +"rope-sequence@npm:^1.3.0": + version: 1.3.4 + resolution: "rope-sequence@npm:1.3.4" + checksum: 10c0/caa90be3d7a7cad155fb354a4679a1280dc9819c81bd319542a0d893a64e152284abb9cc1631d4351b328016a8d6c35a48c912234edfaf5173daef44b2e3609b + languageName: node + linkType: hard + +"run-async@npm:^2.4.0": + version: 2.4.1 + resolution: "run-async@npm:2.4.1" + checksum: 10c0/35a68c8f1d9664f6c7c2e153877ca1d6e4f886e5ca067c25cdd895a6891ff3a1466ee07c63d6a9be306e9619ff7d509494e6d9c129516a36b9fd82263d579ee1 + languageName: node + linkType: hard + +"run-parallel@npm:^1.1.9": + version: 1.2.0 + resolution: "run-parallel@npm:1.2.0" + dependencies: + queue-microtask: "npm:^1.2.2" + checksum: 10c0/200b5ab25b5b8b7113f9901bfe3afc347e19bb7475b267d55ad0eb86a62a46d77510cb0f232507c9e5d497ebda569a08a9867d0d14f57a82ad5564d991588b39 + languageName: node + linkType: hard + +"rxjs@npm:^7.5.5": + version: 7.8.1 + resolution: "rxjs@npm:7.8.1" + dependencies: + tslib: "npm:^2.1.0" + checksum: 10c0/3c49c1ecd66170b175c9cacf5cef67f8914dcbc7cd0162855538d365c83fea631167cacb644b3ce533b2ea0e9a4d0b12175186985f89d75abe73dbd8f7f06f68 + languageName: node + linkType: hard + +"safe-buffer@npm:5.1.2, safe-buffer@npm:~5.1.0, safe-buffer@npm:~5.1.1": + version: 5.1.2 + resolution: "safe-buffer@npm:5.1.2" + checksum: 10c0/780ba6b5d99cc9a40f7b951d47152297d0e260f0df01472a1b99d4889679a4b94a13d644f7dbc4f022572f09ae9005fa2fbb93bbbd83643316f365a3e9a45b21 + languageName: node + linkType: hard + +"safe-buffer@npm:5.2.1, safe-buffer@npm:^5.0.1, safe-buffer@npm:~5.2.0": + version: 5.2.1 + resolution: "safe-buffer@npm:5.2.1" + checksum: 10c0/6501914237c0a86e9675d4e51d89ca3c21ffd6a31642efeba25ad65720bce6921c9e7e974e5be91a786b25aa058b5303285d3c15dbabf983a919f5f630d349f3 + languageName: node + linkType: hard + +"safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0": + version: 2.1.2 + resolution: "safer-buffer@npm:2.1.2" + checksum: 10c0/7e3c8b2e88a1841c9671094bbaeebd94448111dd90a81a1f606f3f67708a6ec57763b3b47f06da09fc6054193e0e6709e77325415dc8422b04497a8070fa02d4 + languageName: node + linkType: hard + +"scheduler@npm:^0.23.2": + version: 0.23.2 + resolution: "scheduler@npm:0.23.2" + dependencies: + loose-envify: "npm:^1.1.0" + checksum: 10c0/26383305e249651d4c58e6705d5f8425f153211aef95f15161c151f7b8de885f24751b377e4a0b3dd42cce09aad3f87a61dab7636859c0d89b7daf1a1e2a5c78 + languageName: node + linkType: hard + +"semver@npm:2 || 3 || 4 || 5, semver@npm:^5.6.0": + version: 5.7.2 + resolution: "semver@npm:5.7.2" + bin: + semver: bin/semver + checksum: 10c0/e4cf10f86f168db772ae95d86ba65b3fd6c5967c94d97c708ccb463b778c2ee53b914cd7167620950fc07faf5a564e6efe903836639e512a1aa15fbc9667fa25 + languageName: node + linkType: hard + +"semver@npm:^6.0.0, semver@npm:^6.3.1": + version: 6.3.1 + resolution: "semver@npm:6.3.1" + bin: + semver: bin/semver.js + checksum: 10c0/e3d79b609071caa78bcb6ce2ad81c7966a46a7431d9d58b8800cfa9cb6a63699b3899a0e4bcce36167a284578212d9ae6942b6929ba4aa5015c079a67751d42d + languageName: node + linkType: hard + +"semver@npm:^7.0.0, semver@npm:^7.1.1, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.3, semver@npm:^7.5.4, semver@npm:^7.6.0": + version: 7.6.3 + resolution: "semver@npm:7.6.3" + bin: + semver: bin/semver.js + checksum: 10c0/88f33e148b210c153873cb08cfe1e281d518aaa9a666d4d148add6560db5cd3c582f3a08ccb91f38d5f379ead256da9931234ed122057f40bb5766e65e58adaf + languageName: node + linkType: hard + +"send@npm:0.18.0": + version: 0.18.0 + resolution: "send@npm:0.18.0" + dependencies: + debug: "npm:2.6.9" + depd: "npm:2.0.0" + destroy: "npm:1.2.0" + encodeurl: "npm:~1.0.2" + escape-html: "npm:~1.0.3" + etag: "npm:~1.8.1" + fresh: "npm:0.5.2" + http-errors: "npm:2.0.0" + mime: "npm:1.6.0" + ms: "npm:2.1.3" + on-finished: "npm:2.4.1" + range-parser: "npm:~1.2.1" + statuses: "npm:2.0.1" + checksum: 10c0/0eb134d6a51fc13bbcb976a1f4214ea1e33f242fae046efc311e80aff66c7a43603e26a79d9d06670283a13000e51be6e0a2cb80ff0942eaf9f1cd30b7ae736a + languageName: node + linkType: hard + +"serve-static@npm:1.15.0": + version: 1.15.0 + resolution: "serve-static@npm:1.15.0" + dependencies: + encodeurl: "npm:~1.0.2" + escape-html: "npm:~1.0.3" + parseurl: "npm:~1.3.3" + send: "npm:0.18.0" + checksum: 10c0/fa9f0e21a540a28f301258dfe1e57bb4f81cd460d28f0e973860477dd4acef946a1f41748b5bd41c73b621bea2029569c935faa38578fd34cd42a9b4947088ba + languageName: node + linkType: hard + +"set-blocking@npm:^2.0.0": + version: 2.0.0 + resolution: "set-blocking@npm:2.0.0" + checksum: 10c0/9f8c1b2d800800d0b589de1477c753492de5c1548d4ade52f57f1d1f5e04af5481554d75ce5e5c43d4004b80a3eb714398d6907027dc0534177b7539119f4454 + languageName: node + linkType: hard + +"set-function-length@npm:^1.2.1": + version: 1.2.2 + resolution: "set-function-length@npm:1.2.2" + dependencies: + define-data-property: "npm:^1.1.4" + es-errors: "npm:^1.3.0" + function-bind: "npm:^1.1.2" + get-intrinsic: "npm:^1.2.4" + gopd: "npm:^1.0.1" + has-property-descriptors: "npm:^1.0.2" + checksum: 10c0/82850e62f412a258b71e123d4ed3873fa9377c216809551192bb6769329340176f109c2eeae8c22a8d386c76739855f78e8716515c818bcaef384b51110f0f3c + languageName: node + linkType: hard + +"setprototypeof@npm:1.2.0": + version: 1.2.0 + resolution: "setprototypeof@npm:1.2.0" + checksum: 10c0/68733173026766fa0d9ecaeb07f0483f4c2dc70ca376b3b7c40b7cda909f94b0918f6c5ad5ce27a9160bdfb475efaa9d5e705a11d8eaae18f9835d20976028bc + languageName: node + linkType: hard + +"shallow-clone@npm:^3.0.0": + version: 3.0.1 + resolution: "shallow-clone@npm:3.0.1" + dependencies: + kind-of: "npm:^6.0.2" + checksum: 10c0/7bab09613a1b9f480c85a9823aebec533015579fa055ba6634aa56ba1f984380670eaf33b8217502931872aa1401c9fcadaa15f9f604d631536df475b05bcf1e + languageName: node + linkType: hard + +"sharp@npm:^0.33.4": + version: 0.33.4 + resolution: "sharp@npm:0.33.4" + dependencies: + "@img/sharp-darwin-arm64": "npm:0.33.4" + "@img/sharp-darwin-x64": "npm:0.33.4" + "@img/sharp-libvips-darwin-arm64": "npm:1.0.2" + "@img/sharp-libvips-darwin-x64": "npm:1.0.2" + "@img/sharp-libvips-linux-arm": "npm:1.0.2" + "@img/sharp-libvips-linux-arm64": "npm:1.0.2" + "@img/sharp-libvips-linux-s390x": "npm:1.0.2" + "@img/sharp-libvips-linux-x64": "npm:1.0.2" + "@img/sharp-libvips-linuxmusl-arm64": "npm:1.0.2" + "@img/sharp-libvips-linuxmusl-x64": "npm:1.0.2" + "@img/sharp-linux-arm": "npm:0.33.4" + "@img/sharp-linux-arm64": "npm:0.33.4" + "@img/sharp-linux-s390x": "npm:0.33.4" + "@img/sharp-linux-x64": "npm:0.33.4" + "@img/sharp-linuxmusl-arm64": "npm:0.33.4" + "@img/sharp-linuxmusl-x64": "npm:0.33.4" + "@img/sharp-wasm32": "npm:0.33.4" + "@img/sharp-win32-ia32": "npm:0.33.4" + "@img/sharp-win32-x64": "npm:0.33.4" + color: "npm:^4.2.3" + detect-libc: "npm:^2.0.3" + semver: "npm:^7.6.0" + dependenciesMeta: + "@img/sharp-darwin-arm64": + optional: true + "@img/sharp-darwin-x64": + optional: true + "@img/sharp-libvips-darwin-arm64": + optional: true + "@img/sharp-libvips-darwin-x64": + optional: true + "@img/sharp-libvips-linux-arm": + optional: true + "@img/sharp-libvips-linux-arm64": + optional: true + "@img/sharp-libvips-linux-s390x": + optional: true + "@img/sharp-libvips-linux-x64": + optional: true + "@img/sharp-libvips-linuxmusl-arm64": + optional: true + "@img/sharp-libvips-linuxmusl-x64": + optional: true + "@img/sharp-linux-arm": + optional: true + "@img/sharp-linux-arm64": + optional: true + "@img/sharp-linux-s390x": + optional: true + "@img/sharp-linux-x64": + optional: true + "@img/sharp-linuxmusl-arm64": + optional: true + "@img/sharp-linuxmusl-x64": + optional: true + "@img/sharp-wasm32": + optional: true + "@img/sharp-win32-ia32": + optional: true + "@img/sharp-win32-x64": + optional: true + checksum: 10c0/428c5c6a84ff8968effe50c2de931002f5f30b9f263e1c026d0384e581673c13088a49322f7748114d3d9be4ae9476a74bf003a3af34743e97ef2f880d1cfe45 + languageName: node + linkType: hard + +"shebang-command@npm:^2.0.0": + version: 2.0.0 + resolution: "shebang-command@npm:2.0.0" + dependencies: + shebang-regex: "npm:^3.0.0" + checksum: 10c0/a41692e7d89a553ef21d324a5cceb5f686d1f3c040759c50aab69688634688c5c327f26f3ecf7001ebfd78c01f3c7c0a11a7c8bfd0a8bc9f6240d4f40b224e4e + languageName: node + linkType: hard + +"shebang-regex@npm:^3.0.0": + version: 3.0.0 + resolution: "shebang-regex@npm:3.0.0" + checksum: 10c0/1dbed0726dd0e1152a92696c76c7f06084eb32a90f0528d11acd764043aacf76994b2fb30aa1291a21bd019d6699164d048286309a278855ee7bec06cf6fb690 + languageName: node + linkType: hard + +"side-channel@npm:^1.0.4": + version: 1.0.6 + resolution: "side-channel@npm:1.0.6" + dependencies: + call-bind: "npm:^1.0.7" + es-errors: "npm:^1.3.0" + get-intrinsic: "npm:^1.2.4" + object-inspect: "npm:^1.13.1" + checksum: 10c0/d2afd163dc733cc0a39aa6f7e39bf0c436293510dbccbff446733daeaf295857dbccf94297092ec8c53e2503acac30f0b78830876f0485991d62a90e9cad305f + languageName: node + linkType: hard + +"signal-exit@npm:3.0.7, signal-exit@npm:^3.0.0, signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.3": + version: 3.0.7 + resolution: "signal-exit@npm:3.0.7" + checksum: 10c0/25d272fa73e146048565e08f3309d5b942c1979a6f4a58a8c59d5fa299728e9c2fcd1a759ec870863b1fd38653670240cd420dad2ad9330c71f36608a6a1c912 + languageName: node + linkType: hard + +"signal-exit@npm:^4.0.1": + version: 4.1.0 + resolution: "signal-exit@npm:4.1.0" + checksum: 10c0/41602dce540e46d599edba9d9860193398d135f7ff72cab629db5171516cfae628d21e7bfccde1bbfdf11c48726bc2a6d1a8fb8701125852fbfda7cf19c6aa83 + languageName: node + linkType: hard + +"signale@npm:^1.4.0": + version: 1.4.0 + resolution: "signale@npm:1.4.0" + dependencies: + chalk: "npm:^2.3.2" + figures: "npm:^2.0.0" + pkg-conf: "npm:^2.1.0" + checksum: 10c0/3b637421368a30805da3948f82350cb9959ddfb19073f44609495384b98baba1c62b1c5c094db57000836c8bc84c6c05c979aa7e072ceeaaf0032d7991b329c7 + languageName: node + linkType: hard + +"sigstore@npm:^2.2.0": + version: 2.3.1 + resolution: "sigstore@npm:2.3.1" + dependencies: + "@sigstore/bundle": "npm:^2.3.2" + "@sigstore/core": "npm:^1.0.0" + "@sigstore/protobuf-specs": "npm:^0.3.2" + "@sigstore/sign": "npm:^2.3.2" + "@sigstore/tuf": "npm:^2.3.4" + "@sigstore/verify": "npm:^1.2.1" + checksum: 10c0/8906b1074130d430d707e46f15c66eb6996891dc0d068705f1884fb1251a4a367f437267d44102cdebcee34f1768b3f30131a2ec8fb7aac74ba250903a459aa7 + languageName: node + linkType: hard + +"simple-swizzle@npm:^0.2.2": + version: 0.2.2 + resolution: "simple-swizzle@npm:0.2.2" + dependencies: + is-arrayish: "npm:^0.3.1" + checksum: 10c0/df5e4662a8c750bdba69af4e8263c5d96fe4cd0f9fe4bdfa3cbdeb45d2e869dff640beaaeb1ef0e99db4d8d2ec92f85508c269f50c972174851bc1ae5bd64308 + languageName: node + linkType: hard + +"slash@npm:3.0.0, slash@npm:^3.0.0": + version: 3.0.0 + resolution: "slash@npm:3.0.0" + checksum: 10c0/e18488c6a42bdfd4ac5be85b2ced3ccd0224773baae6ad42cfbb9ec74fc07f9fa8396bd35ee638084ead7a2a0818eb5e7151111544d4731ce843019dab4be47b + languageName: node + linkType: hard + +"slick@npm:^1.12.2": + version: 1.12.2 + resolution: "slick@npm:1.12.2" + checksum: 10c0/fea97c36b2bdcd1b80caea150cd8135dc9d3ffe659bbe04fa6f4b4dff373f5d5aef09a8ef384b331c3fdd9567faf447b75b850ab35d2c69ff8a8a92def3d49e1 + languageName: node + linkType: hard + +"smart-buffer@npm:^4.2.0": + version: 4.2.0 + resolution: "smart-buffer@npm:4.2.0" + checksum: 10c0/a16775323e1404dd43fabafe7460be13a471e021637bc7889468eb45ce6a6b207261f454e4e530a19500cc962c4cc5348583520843b363f4193cee5c00e1e539 + languageName: node + linkType: hard + +"snake-case@npm:^3.0.4": + version: 3.0.4 + resolution: "snake-case@npm:3.0.4" + dependencies: + dot-case: "npm:^3.0.4" + tslib: "npm:^2.0.3" + checksum: 10c0/ab19a913969f58f4474fe9f6e8a026c8a2142a01f40b52b79368068343177f818cdfef0b0c6b9558f298782441d5ca8ed5932eb57822439fad791d866e62cecd + languageName: node + linkType: hard + +"socks-proxy-agent@npm:^8.0.3": + version: 8.0.4 + resolution: "socks-proxy-agent@npm:8.0.4" + dependencies: + agent-base: "npm:^7.1.1" + debug: "npm:^4.3.4" + socks: "npm:^2.8.3" + checksum: 10c0/345593bb21b95b0508e63e703c84da11549f0a2657d6b4e3ee3612c312cb3a907eac10e53b23ede3557c6601d63252103494caa306b66560f43af7b98f53957a + languageName: node + linkType: hard + +"socks@npm:^2.8.3": + version: 2.8.3 + resolution: "socks@npm:2.8.3" + dependencies: + ip-address: "npm:^9.0.5" + smart-buffer: "npm:^4.2.0" + checksum: 10c0/d54a52bf9325165770b674a67241143a3d8b4e4c8884560c4e0e078aace2a728dffc7f70150660f51b85797c4e1a3b82f9b7aa25e0a0ceae1a243365da5c51a7 + languageName: node + linkType: hard + +"sonner@npm:^1.5.0": + version: 1.5.0 + resolution: "sonner@npm:1.5.0" + peerDependencies: + react: ^18.0.0 + react-dom: ^18.0.0 + checksum: 10c0/9561b5861bede7f874cc442e447a68c8bfa6e4eadad603bc04e38db35a8b8108741f595a12c9856742062bae230ffedf73122015940491f482c5aa9e68ee85e0 + languageName: node + linkType: hard + +"sort-keys@npm:^2.0.0": + version: 2.0.0 + resolution: "sort-keys@npm:2.0.0" + dependencies: + is-plain-obj: "npm:^1.0.0" + checksum: 10c0/c11a6313995cb67ccf35fed4b1f6734176cc1d1e350ee311c061a2340ada4f7e23b046db064d518b63adba98c0f763739920c59fb4659a0b8482ec7a1f255081 + languageName: node + linkType: hard + +"source-map-js@npm:^1.0.1, source-map-js@npm:^1.0.2, source-map-js@npm:^1.2.0": + version: 1.2.0 + resolution: "source-map-js@npm:1.2.0" + checksum: 10c0/7e5f896ac10a3a50fe2898e5009c58ff0dc102dcb056ed27a354623a0ece8954d4b2649e1a1b2b52ef2e161d26f8859c7710350930751640e71e374fe2d321a4 + languageName: node + linkType: hard + +"source-map-support@npm:^0.5.12": + version: 0.5.21 + resolution: "source-map-support@npm:0.5.21" + dependencies: + buffer-from: "npm:^1.0.0" + source-map: "npm:^0.6.0" + checksum: 10c0/9ee09942f415e0f721d6daad3917ec1516af746a8120bba7bb56278707a37f1eb8642bde456e98454b8a885023af81a16e646869975f06afc1a711fb90484e7d + languageName: node + linkType: hard + +"source-map@npm:^0.6.0, source-map@npm:^0.6.1, source-map@npm:~0.6.0": + version: 0.6.1 + resolution: "source-map@npm:0.6.1" + checksum: 10c0/ab55398007c5e5532957cb0beee2368529618ac0ab372d789806f5718123cc4367d57de3904b4e6a4170eb5a0b0f41373066d02ca0735a0c4d75c7d328d3e011 + languageName: node + linkType: hard + +"space-separated-tokens@npm:^1.0.0": + version: 1.1.5 + resolution: "space-separated-tokens@npm:1.1.5" + checksum: 10c0/3ee0a6905f89e1ffdfe474124b1ade9fe97276a377a0b01350bc079b6ec566eb5b219e26064cc5b7f3899c05bde51ffbc9154290b96eaf82916a1e2c2c13ead9 + languageName: node + linkType: hard + +"spdx-correct@npm:^3.0.0": + version: 3.2.0 + resolution: "spdx-correct@npm:3.2.0" + dependencies: + spdx-expression-parse: "npm:^3.0.0" + spdx-license-ids: "npm:^3.0.0" + checksum: 10c0/49208f008618b9119208b0dadc9208a3a55053f4fd6a0ae8116861bd22696fc50f4142a35ebfdb389e05ccf2de8ad142573fefc9e26f670522d899f7b2fe7386 + languageName: node + linkType: hard + +"spdx-exceptions@npm:^2.1.0": + version: 2.5.0 + resolution: "spdx-exceptions@npm:2.5.0" + checksum: 10c0/37217b7762ee0ea0d8b7d0c29fd48b7e4dfb94096b109d6255b589c561f57da93bf4e328c0290046115961b9209a8051ad9f525e48d433082fc79f496a4ea940 + languageName: node + linkType: hard + +"spdx-expression-parse@npm:^3.0.0": + version: 3.0.1 + resolution: "spdx-expression-parse@npm:3.0.1" + dependencies: + spdx-exceptions: "npm:^2.1.0" + spdx-license-ids: "npm:^3.0.0" + checksum: 10c0/6f8a41c87759fa184a58713b86c6a8b028250f158159f1d03ed9d1b6ee4d9eefdc74181c8ddc581a341aa971c3e7b79e30b59c23b05d2436d5de1c30bdef7171 + languageName: node + linkType: hard + +"spdx-license-ids@npm:^3.0.0": + version: 3.0.18 + resolution: "spdx-license-ids@npm:3.0.18" + checksum: 10c0/c64ba03d4727191c8fdbd001f137d6ab51386c350d5516be8a4576c2e74044cb27bc8a758f6a04809da986cc0b14213f069b04de72caccecbc9f733753ccde32 + languageName: node + linkType: hard + +"split2@npm:^3.2.2": + version: 3.2.2 + resolution: "split2@npm:3.2.2" + dependencies: + readable-stream: "npm:^3.0.0" + checksum: 10c0/2dad5603c52b353939befa3e2f108f6e3aff42b204ad0f5f16dd12fd7c2beab48d117184ce6f7c8854f9ee5ffec6faae70d243711dd7d143a9f635b4a285de4e + languageName: node + linkType: hard + +"split@npm:^1.0.1": + version: 1.0.1 + resolution: "split@npm:1.0.1" + dependencies: + through: "npm:2" + checksum: 10c0/7f489e7ed5ff8a2e43295f30a5197ffcb2d6202c9cf99357f9690d645b19c812bccf0be3ff336fea5054cda17ac96b91d67147d95dbfc31fbb5804c61962af85 + languageName: node + linkType: hard + +"sprintf-js@npm:^1.1.3": + version: 1.1.3 + resolution: "sprintf-js@npm:1.1.3" + checksum: 10c0/09270dc4f30d479e666aee820eacd9e464215cdff53848b443964202bf4051490538e5dd1b42e1a65cf7296916ca17640aebf63dae9812749c7542ee5f288dec + languageName: node + linkType: hard + +"sprintf-js@npm:~1.0.2": + version: 1.0.3 + resolution: "sprintf-js@npm:1.0.3" + checksum: 10c0/ecadcfe4c771890140da5023d43e190b7566d9cf8b2d238600f31bec0fc653f328da4450eb04bd59a431771a8e9cc0e118f0aa3974b683a4981b4e07abc2a5bb + languageName: node + linkType: hard + +"ssri@npm:^10.0.0, ssri@npm:^10.0.6": + version: 10.0.6 + resolution: "ssri@npm:10.0.6" + dependencies: + minipass: "npm:^7.0.3" + checksum: 10c0/e5a1e23a4057a86a97971465418f22ea89bd439ac36ade88812dd920e4e61873e8abd6a9b72a03a67ef50faa00a2daf1ab745c5a15b46d03e0544a0296354227 + languageName: node + linkType: hard + +"standard-as-callback@npm:^2.1.0": + version: 2.1.0 + resolution: "standard-as-callback@npm:2.1.0" + checksum: 10c0/012677236e3d3fdc5689d29e64ea8a599331c4babe86956bf92fc5e127d53f85411c5536ee0079c52c43beb0026b5ce7aa1d834dd35dd026e82a15d1bcaead1f + languageName: node + linkType: hard + +"state-local@npm:^1.0.6": + version: 1.0.7 + resolution: "state-local@npm:1.0.7" + checksum: 10c0/8dc7daeac71844452fafb514a6d6b6f40d7e2b33df398309ea1c7b3948d6110c57f112b7196500a10c54fdde40291488c52c875575670fb5c819602deca48bd9 + languageName: node + linkType: hard + +"statuses@npm:2.0.1": + version: 2.0.1 + resolution: "statuses@npm:2.0.1" + checksum: 10c0/34378b207a1620a24804ce8b5d230fea0c279f00b18a7209646d5d47e419d1cc23e7cbf33a25a1e51ac38973dc2ac2e1e9c647a8e481ef365f77668d72becfd0 + languageName: node + linkType: hard + +"streamsearch@npm:^1.1.0": + version: 1.1.0 + resolution: "streamsearch@npm:1.1.0" + checksum: 10c0/fbd9aecc2621364384d157f7e59426f4bfd385e8b424b5aaa79c83a6f5a1c8fd2e4e3289e95de1eb3511cb96bb333d6281a9919fafce760e4edb35b2cd2facab + languageName: node + linkType: hard + +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": + version: 4.2.3 + resolution: "string-width@npm:4.2.3" + dependencies: + emoji-regex: "npm:^8.0.0" + is-fullwidth-code-point: "npm:^3.0.0" + strip-ansi: "npm:^6.0.1" + checksum: 10c0/1e525e92e5eae0afd7454086eed9c818ee84374bb80328fc41217ae72ff5f065ef1c9d7f72da41de40c75fa8bb3dee63d92373fd492c84260a552c636392a47b + languageName: node + linkType: hard + +"string-width@npm:^5.0.1, string-width@npm:^5.1.2": + version: 5.1.2 + resolution: "string-width@npm:5.1.2" + dependencies: + eastasianwidth: "npm:^0.2.0" + emoji-regex: "npm:^9.2.2" + strip-ansi: "npm:^7.0.1" + checksum: 10c0/ab9c4264443d35b8b923cbdd513a089a60de339216d3b0ed3be3ba57d6880e1a192b70ae17225f764d7adbf5994e9bb8df253a944736c15a0240eff553c678ca + languageName: node + linkType: hard + +"string_decoder@npm:^1.1.1": + version: 1.3.0 + resolution: "string_decoder@npm:1.3.0" + dependencies: + safe-buffer: "npm:~5.2.0" + checksum: 10c0/810614ddb030e271cd591935dcd5956b2410dd079d64ff92a1844d6b7588bf992b3e1b69b0f4d34a3e06e0bd73046ac646b5264c1987b20d0601f81ef35d731d + languageName: node + linkType: hard + +"string_decoder@npm:~1.1.1": + version: 1.1.1 + resolution: "string_decoder@npm:1.1.1" + dependencies: + safe-buffer: "npm:~5.1.0" + checksum: 10c0/b4f89f3a92fd101b5653ca3c99550e07bdf9e13b35037e9e2a1c7b47cec4e55e06ff3fc468e314a0b5e80bfbaf65c1ca5a84978764884ae9413bec1fc6ca924e + languageName: node + linkType: hard + +"strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": + version: 6.0.1 + resolution: "strip-ansi@npm:6.0.1" + dependencies: + ansi-regex: "npm:^5.0.1" + checksum: 10c0/1ae5f212a126fe5b167707f716942490e3933085a5ff6c008ab97ab2f272c8025d3aa218b7bd6ab25729ca20cc81cddb252102f8751e13482a5199e873680952 + languageName: node + linkType: hard + +"strip-ansi@npm:^7.0.1": + version: 7.1.0 + resolution: "strip-ansi@npm:7.1.0" + dependencies: + ansi-regex: "npm:^6.0.1" + checksum: 10c0/a198c3762e8832505328cbf9e8c8381de14a4fa50a4f9b2160138158ea88c0f5549fb50cb13c651c3088f47e63a108b34622ec18c0499b6c8c3a5ddf6b305ac4 + languageName: node + linkType: hard + +"strip-bom@npm:^3.0.0": + version: 3.0.0 + resolution: "strip-bom@npm:3.0.0" + checksum: 10c0/51201f50e021ef16672593d7434ca239441b7b760e905d9f33df6e4f3954ff54ec0e0a06f100d028af0982d6f25c35cd5cda2ce34eaebccd0250b8befb90d8f1 + languageName: node + linkType: hard + +"strip-bom@npm:^4.0.0": + version: 4.0.0 + resolution: "strip-bom@npm:4.0.0" + checksum: 10c0/26abad1172d6bc48985ab9a5f96c21e440f6e7e476686de49be813b5a59b3566dccb5c525b831ec54fe348283b47f3ffb8e080bc3f965fde12e84df23f6bb7ef + languageName: node + linkType: hard + +"strip-final-newline@npm:^2.0.0": + version: 2.0.0 + resolution: "strip-final-newline@npm:2.0.0" + checksum: 10c0/bddf8ccd47acd85c0e09ad7375409d81653f645fda13227a9d459642277c253d877b68f2e5e4d819fe75733b0e626bac7e954c04f3236f6d196f79c94fa4a96f + languageName: node + linkType: hard + +"strip-indent@npm:^3.0.0": + version: 3.0.0 + resolution: "strip-indent@npm:3.0.0" + dependencies: + min-indent: "npm:^1.0.0" + checksum: 10c0/ae0deaf41c8d1001c5d4fbe16cb553865c1863da4fae036683b474fa926af9fc121e155cb3fc57a68262b2ae7d5b8420aa752c97a6428c315d00efe2a3875679 + languageName: node + linkType: hard + +"strip-json-comments@npm:^2.0.0": + version: 2.0.1 + resolution: "strip-json-comments@npm:2.0.1" + checksum: 10c0/b509231cbdee45064ff4f9fd73609e2bcc4e84a4d508e9dd0f31f70356473fde18abfb5838c17d56fb236f5a06b102ef115438de0600b749e818a35fbbc48c43 + languageName: node + linkType: hard + +"strnum@npm:^1.0.5": + version: 1.0.5 + resolution: "strnum@npm:1.0.5" + checksum: 10c0/64fb8cc2effbd585a6821faa73ad97d4b553c8927e49086a162ffd2cc818787643390b89d567460a8e74300148d11ac052e21c921ef2049f2987f4b1b89a7ff1 + languageName: node + linkType: hard + +"strong-log-transformer@npm:2.1.0, strong-log-transformer@npm:^2.1.0": + version: 2.1.0 + resolution: "strong-log-transformer@npm:2.1.0" + dependencies: + duplexer: "npm:^0.1.1" + minimist: "npm:^1.2.0" + through: "npm:^2.3.4" + bin: + sl-log-transformer: bin/sl-log-transformer.js + checksum: 10c0/3c3b8aa8f34d661910563ff996412e2f527fc814e699a376854b554d4a4294ab7e285b4e2c08a080a7b19c5600a9b93a98798d3ac600fe3de545ca6605c07829 + languageName: node + linkType: hard + +"styled-jsx@npm:5.1.1": + version: 5.1.1 + resolution: "styled-jsx@npm:5.1.1" + dependencies: + client-only: "npm:0.0.1" + peerDependencies: + react: ">= 16.8.0 || 17.x.x || ^18.0.0-0" + peerDependenciesMeta: + "@babel/core": + optional: true + babel-plugin-macros: + optional: true + checksum: 10c0/42655cdadfa5388f8a48bb282d6b450df7d7b8cf066ac37038bd0499d3c9f084815ebd9ff9dfa12a218fd4441338851db79603498d7557207009c1cf4d609835 + languageName: node + linkType: hard + +"sucrase@npm:^3.32.0": + version: 3.35.0 + resolution: "sucrase@npm:3.35.0" + dependencies: + "@jridgewell/gen-mapping": "npm:^0.3.2" + commander: "npm:^4.0.0" + glob: "npm:^10.3.10" + lines-and-columns: "npm:^1.1.6" + mz: "npm:^2.7.0" + pirates: "npm:^4.0.1" + ts-interface-checker: "npm:^0.1.9" + bin: + sucrase: bin/sucrase + sucrase-node: bin/sucrase-node + checksum: 10c0/ac85f3359d2c2ecbf5febca6a24ae9bf96c931f05fde533c22a94f59c6a74895e5d5f0e871878dfd59c2697a75ebb04e4b2224ef0bfc24ca1210735c2ec191ef + languageName: node + linkType: hard + +"supports-color@npm:^5.3.0": + version: 5.5.0 + resolution: "supports-color@npm:5.5.0" + dependencies: + has-flag: "npm:^3.0.0" + checksum: 10c0/6ae5ff319bfbb021f8a86da8ea1f8db52fac8bd4d499492e30ec17095b58af11f0c55f8577390a749b1c4dde691b6a0315dab78f5f54c9b3d83f8fb5905c1c05 + languageName: node + linkType: hard + +"supports-color@npm:^7.1.0": + version: 7.2.0 + resolution: "supports-color@npm:7.2.0" + dependencies: + has-flag: "npm:^4.0.0" + checksum: 10c0/afb4c88521b8b136b5f5f95160c98dee7243dc79d5432db7efc27efb219385bbc7d9427398e43dd6cc730a0f87d5085ce1652af7efbe391327bc0a7d0f7fc124 + languageName: node + linkType: hard + +"supports-preserve-symlinks-flag@npm:^1.0.0": + version: 1.0.0 + resolution: "supports-preserve-symlinks-flag@npm:1.0.0" + checksum: 10c0/6c4032340701a9950865f7ae8ef38578d8d7053f5e10518076e6554a9381fa91bd9c6850193695c141f32b21f979c985db07265a758867bac95de05f7d8aeb39 + languageName: node + linkType: hard + +"svg-parser@npm:^2.0.4": + version: 2.0.4 + resolution: "svg-parser@npm:2.0.4" + checksum: 10c0/02f6cb155dd7b63ebc2f44f36365bc294543bebb81b614b7628f1af3c54ab64f7e1cec20f06e252bf95bdde78441ae295a412c68ad1678f16a6907d924512b7a + languageName: node + linkType: hard + +"svgo@npm:^3.0.2": + version: 3.3.2 + resolution: "svgo@npm:3.3.2" + dependencies: + "@trysound/sax": "npm:0.2.0" + commander: "npm:^7.2.0" + css-select: "npm:^5.1.0" + css-tree: "npm:^2.3.1" + css-what: "npm:^6.1.0" + csso: "npm:^5.0.5" + picocolors: "npm:^1.0.0" + bin: + svgo: ./bin/svgo + checksum: 10c0/a6badbd3d1d6dbb177f872787699ab34320b990d12e20798ecae915f0008796a0f3c69164f1485c9def399e0ce0a5683eb4a8045e51a5e1c364bb13a0d9f79e1 + languageName: node + linkType: hard + +"swr@npm:2.2.5": + version: 2.2.5 + resolution: "swr@npm:2.2.5" + dependencies: + client-only: "npm:^0.0.1" + use-sync-external-store: "npm:^1.2.0" + peerDependencies: + react: ^16.11.0 || ^17.0.0 || ^18.0.0 + checksum: 10c0/731488d609ac6db60626632e3f76b046f28400b44504b3dfa69231a645127579b1add7a1595e5a6c718e24c80f1399506883bb456ca83c1b621357a0bf5a2a94 + languageName: node + linkType: hard + +"tailwind-scrollbar@npm:^3.1.0": + version: 3.1.0 + resolution: "tailwind-scrollbar@npm:3.1.0" + peerDependencies: + tailwindcss: 3.x + checksum: 10c0/dad4d5dcc37f09806cb1b5380d7908d113d139638ac606644ba0c689dffb2d72250f6997c0091c2ba596346390b31742859a20933c2d8543f8103e2ddfc3fb82 + languageName: node + linkType: hard + +"tailwindcss@npm:^3.4.6": + version: 3.4.6 + resolution: "tailwindcss@npm:3.4.6" + dependencies: + "@alloc/quick-lru": "npm:^5.2.0" + arg: "npm:^5.0.2" + chokidar: "npm:^3.5.3" + didyoumean: "npm:^1.2.2" + dlv: "npm:^1.1.3" + fast-glob: "npm:^3.3.0" + glob-parent: "npm:^6.0.2" + is-glob: "npm:^4.0.3" + jiti: "npm:^1.21.0" + lilconfig: "npm:^2.1.0" + micromatch: "npm:^4.0.5" + normalize-path: "npm:^3.0.0" + object-hash: "npm:^3.0.0" + picocolors: "npm:^1.0.0" + postcss: "npm:^8.4.23" + postcss-import: "npm:^15.1.0" + postcss-js: "npm:^4.0.1" + postcss-load-config: "npm:^4.0.1" + postcss-nested: "npm:^6.0.1" + postcss-selector-parser: "npm:^6.0.11" + resolve: "npm:^1.22.2" + sucrase: "npm:^3.32.0" + bin: + tailwind: lib/cli.js + tailwindcss: lib/cli.js + checksum: 10c0/8d82b697ecf41d8cd100ab3369e3cbcb30e350afc5b595a000b201ba666628a68543154297f96c5b2a3f2614f37bb981af4766556ebaae832079fa9a436e8563 + languageName: node + linkType: hard + +"tar-stream@npm:~2.2.0": + version: 2.2.0 + resolution: "tar-stream@npm:2.2.0" + dependencies: + bl: "npm:^4.0.3" + end-of-stream: "npm:^1.4.1" + fs-constants: "npm:^1.0.0" + inherits: "npm:^2.0.3" + readable-stream: "npm:^3.1.1" + checksum: 10c0/2f4c910b3ee7196502e1ff015a7ba321ec6ea837667220d7bcb8d0852d51cb04b87f7ae471008a6fb8f5b1a1b5078f62f3a82d30c706f20ada1238ac797e7692 + languageName: node + linkType: hard + +"tar@npm:6.2.1, tar@npm:^6.1.11, tar@npm:^6.2.1": + version: 6.2.1 + resolution: "tar@npm:6.2.1" + dependencies: + chownr: "npm:^2.0.0" + fs-minipass: "npm:^2.0.0" + minipass: "npm:^5.0.0" + minizlib: "npm:^2.1.1" + mkdirp: "npm:^1.0.3" + yallist: "npm:^4.0.0" + checksum: 10c0/a5eca3eb50bc11552d453488344e6507156b9193efd7635e98e867fab275d527af53d8866e2370cd09dfe74378a18111622ace35af6a608e5223a7d27fe99537 + languageName: node + linkType: hard + +"temp-dir@npm:1.0.0": + version: 1.0.0 + resolution: "temp-dir@npm:1.0.0" + checksum: 10c0/648669d5e154d1961217784c786acadccf0156519c19e0aceda7edc76f5bdfa32a40dd7f88ebea9238ed6e3dedf08b846161916c8947058c384761351be90a8e + languageName: node + linkType: hard + +"text-extensions@npm:^1.0.0": + version: 1.9.0 + resolution: "text-extensions@npm:1.9.0" + checksum: 10c0/9ad5a9f723a871e2d884e132d7e93f281c60b5759c95f3f6b04704856548715d93a36c10dbaf5f12b91bf405f0cf3893bf169d4d143c0f5509563b992d385443 + languageName: node + linkType: hard + +"thenify-all@npm:^1.0.0": + version: 1.6.0 + resolution: "thenify-all@npm:1.6.0" + dependencies: + thenify: "npm:>= 3.1.0 < 4" + checksum: 10c0/9b896a22735e8122754fe70f1d65f7ee691c1d70b1f116fda04fea103d0f9b356e3676cb789506e3909ae0486a79a476e4914b0f92472c2e093d206aed4b7d6b + languageName: node + linkType: hard + +"thenify@npm:>= 3.1.0 < 4": + version: 3.3.1 + resolution: "thenify@npm:3.3.1" + dependencies: + any-promise: "npm:^1.0.0" + checksum: 10c0/f375aeb2b05c100a456a30bc3ed07ef03a39cbdefe02e0403fb714b8c7e57eeaad1a2f5c4ecfb9ce554ce3db9c2b024eba144843cd9e344566d9fcee73b04767 + languageName: node + linkType: hard + +"through2@npm:^2.0.0": + version: 2.0.5 + resolution: "through2@npm:2.0.5" + dependencies: + readable-stream: "npm:~2.3.6" + xtend: "npm:~4.0.1" + checksum: 10c0/cbfe5b57943fa12b4f8c043658c2a00476216d79c014895cef1ac7a1d9a8b31f6b438d0e53eecbb81054b93128324a82ecd59ec1a4f91f01f7ac113dcb14eade + languageName: node + linkType: hard + +"through@npm:2, through@npm:>=2.2.7 <3, through@npm:^2.3.4, through@npm:^2.3.6": + version: 2.3.8 + resolution: "through@npm:2.3.8" + checksum: 10c0/4b09f3774099de0d4df26d95c5821a62faee32c7e96fb1f4ebd54a2d7c11c57fe88b0a0d49cf375de5fee5ae6bf4eb56dbbf29d07366864e2ee805349970d3cc + languageName: node + linkType: hard + +"tiny-invariant@npm:^1.3.1": + version: 1.3.3 + resolution: "tiny-invariant@npm:1.3.3" + checksum: 10c0/65af4a07324b591a059b35269cd696aba21bef2107f29b9f5894d83cc143159a204b299553435b03874ebb5b94d019afa8b8eff241c8a4cfee95872c2e1c1c4a + languageName: node + linkType: hard + +"tippy.js@npm:^6.3.1, tippy.js@npm:^6.3.7": + version: 6.3.7 + resolution: "tippy.js@npm:6.3.7" + dependencies: + "@popperjs/core": "npm:^2.9.0" + checksum: 10c0/ec3677beb8caec791ee1f715663f28f42d60e0f7250074a047d13d5e6db95fdb6d26d8a3ac16cecb4ebcaf33ae919dbc889cf97948d115e8d3c81518c911b379 + languageName: node + linkType: hard + +"tmp@npm:^0.0.33": + version: 0.0.33 + resolution: "tmp@npm:0.0.33" + dependencies: + os-tmpdir: "npm:~1.0.2" + checksum: 10c0/69863947b8c29cabad43fe0ce65cec5bb4b481d15d4b4b21e036b060b3edbf3bc7a5541de1bacb437bb3f7c4538f669752627fdf9b4aaf034cebd172ba373408 + languageName: node + linkType: hard + +"tmp@npm:~0.2.1": + version: 0.2.3 + resolution: "tmp@npm:0.2.3" + checksum: 10c0/3e809d9c2f46817475b452725c2aaa5d11985cf18d32a7a970ff25b568438e2c076c2e8609224feef3b7923fa9749b74428e3e634f6b8e520c534eef2fd24125 + languageName: node + linkType: hard + +"to-fast-properties@npm:^2.0.0": + version: 2.0.0 + resolution: "to-fast-properties@npm:2.0.0" + checksum: 10c0/b214d21dbfb4bce3452b6244b336806ffea9c05297148d32ebb428d5c43ce7545bdfc65a1ceb58c9ef4376a65c0cb2854d645f33961658b3e3b4f84910ddcdd7 + languageName: node + linkType: hard + +"to-regex-range@npm:^5.0.1": + version: 5.0.1 + resolution: "to-regex-range@npm:5.0.1" + dependencies: + is-number: "npm:^7.0.0" + checksum: 10c0/487988b0a19c654ff3e1961b87f471702e708fa8a8dd02a298ef16da7206692e8552a0250e8b3e8759270f62e9d8314616f6da274734d3b558b1fc7b7724e892 + languageName: node + linkType: hard + +"toidentifier@npm:1.0.1": + version: 1.0.1 + resolution: "toidentifier@npm:1.0.1" + checksum: 10c0/93937279934bd66cc3270016dd8d0afec14fb7c94a05c72dc57321f8bd1fa97e5bea6d1f7c89e728d077ca31ea125b78320a616a6c6cd0e6b9cb94cb864381c1 + languageName: node + linkType: hard + +"tr46@npm:~0.0.3": + version: 0.0.3 + resolution: "tr46@npm:0.0.3" + checksum: 10c0/047cb209a6b60c742f05c9d3ace8fa510bff609995c129a37ace03476a9b12db4dbf975e74600830ef0796e18882b2381fb5fb1f6b4f96b832c374de3ab91a11 + languageName: node + linkType: hard + +"tree-kill@npm:^1.2.2": + version: 1.2.2 + resolution: "tree-kill@npm:1.2.2" + bin: + tree-kill: cli.js + checksum: 10c0/7b1b7c7f17608a8f8d20a162e7957ac1ef6cd1636db1aba92f4e072dc31818c2ff0efac1e3d91064ede67ed5dc57c565420531a8134090a12ac10cf792ab14d2 + languageName: node + linkType: hard + +"treeverse@npm:^3.0.0": + version: 3.0.0 + resolution: "treeverse@npm:3.0.0" + checksum: 10c0/286479b9c05a8fb0538ee7d67a5502cea7704f258057c784c9c1118a2f598788b2c0f7a8d89e74648af88af0225b31766acecd78e6060736f09b21dd3fa255db + languageName: node + linkType: hard + +"trim-newlines@npm:^3.0.0": + version: 3.0.1 + resolution: "trim-newlines@npm:3.0.1" + checksum: 10c0/03cfefde6c59ff57138412b8c6be922ecc5aec30694d784f2a65ef8dcbd47faef580b7de0c949345abdc56ec4b4abf64dd1e5aea619b200316e471a3dd5bf1f6 + languageName: node + linkType: hard + +"ts-interface-checker@npm:^0.1.9": + version: 0.1.13 + resolution: "ts-interface-checker@npm:0.1.13" + checksum: 10c0/232509f1b84192d07b81d1e9b9677088e590ac1303436da1e92b296e9be8e31ea042e3e1fd3d29b1742ad2c959e95afe30f63117b8f1bc3a3850070a5142fea7 + languageName: node + linkType: hard + +"ts-node-dev@npm:^2.0.0": + version: 2.0.0 + resolution: "ts-node-dev@npm:2.0.0" + dependencies: + chokidar: "npm:^3.5.1" + dynamic-dedupe: "npm:^0.3.0" + minimist: "npm:^1.2.6" + mkdirp: "npm:^1.0.4" + resolve: "npm:^1.0.0" + rimraf: "npm:^2.6.1" + source-map-support: "npm:^0.5.12" + tree-kill: "npm:^1.2.2" + ts-node: "npm:^10.4.0" + tsconfig: "npm:^7.0.0" + peerDependencies: + node-notifier: "*" + typescript: "*" + peerDependenciesMeta: + node-notifier: + optional: true + bin: + ts-node-dev: lib/bin.js + tsnd: lib/bin.js + checksum: 10c0/34f81407ede9284eccf47139e22bc85511c6d70e2b8dfae91c917ababc09ba947cc0791549ee7b2e5a69d26de40eedb23c6bdb4fac689ed07a302813bf966faa + languageName: node + linkType: hard + +"ts-node@npm:^10.4.0": + version: 10.9.2 + resolution: "ts-node@npm:10.9.2" + dependencies: + "@cspotcode/source-map-support": "npm:^0.8.0" + "@tsconfig/node10": "npm:^1.0.7" + "@tsconfig/node12": "npm:^1.0.7" + "@tsconfig/node14": "npm:^1.0.0" + "@tsconfig/node16": "npm:^1.0.2" + acorn: "npm:^8.4.1" + acorn-walk: "npm:^8.1.1" + arg: "npm:^4.1.0" + create-require: "npm:^1.1.0" + diff: "npm:^4.0.1" + make-error: "npm:^1.1.1" + v8-compile-cache-lib: "npm:^3.0.1" + yn: "npm:3.1.1" + peerDependencies: + "@swc/core": ">=1.2.50" + "@swc/wasm": ">=1.2.50" + "@types/node": "*" + typescript: ">=2.7" + peerDependenciesMeta: + "@swc/core": + optional: true + "@swc/wasm": + optional: true + bin: + ts-node: dist/bin.js + ts-node-cwd: dist/bin-cwd.js + ts-node-esm: dist/bin-esm.js + ts-node-script: dist/bin-script.js + ts-node-transpile-only: dist/bin-transpile.js + ts-script: dist/bin-script-deprecated.js + checksum: 10c0/5f29938489f96982a25ba650b64218e83a3357d76f7bede80195c65ab44ad279c8357264639b7abdd5d7e75fc269a83daa0e9c62fd8637a3def67254ecc9ddc2 + languageName: node + linkType: hard + +"tsconfig-paths@npm:^4.1.2": + version: 4.2.0 + resolution: "tsconfig-paths@npm:4.2.0" + dependencies: + json5: "npm:^2.2.2" + minimist: "npm:^1.2.6" + strip-bom: "npm:^3.0.0" + checksum: 10c0/09a5877402d082bb1134930c10249edeebc0211f36150c35e1c542e5b91f1047b1ccf7da1e59babca1ef1f014c525510f4f870de7c9bda470c73bb4e2721b3ea + languageName: node + linkType: hard + +"tsconfig@npm:^7.0.0": + version: 7.0.0 + resolution: "tsconfig@npm:7.0.0" + dependencies: + "@types/strip-bom": "npm:^3.0.0" + "@types/strip-json-comments": "npm:0.0.30" + strip-bom: "npm:^3.0.0" + strip-json-comments: "npm:^2.0.0" + checksum: 10c0/7a5dec94b9e42017d93041b1962c174afde00fd8f3066eea81a5e5b743065e95f3bedebff0edbe215b2517f8cdace8c9f15651a78d5eb7409cad2fc107e5eb98 + languageName: node + linkType: hard + +"tslib@npm:^2.0.0, tslib@npm:^2.0.3, tslib@npm:^2.1.0, tslib@npm:^2.3.0, tslib@npm:^2.4.0, tslib@npm:^2.6.2": + version: 2.6.3 + resolution: "tslib@npm:2.6.3" + checksum: 10c0/2598aef53d9dbe711af75522464b2104724d6467b26a60f2bdac8297d2b5f1f6b86a71f61717384aa8fd897240467aaa7bcc36a0700a0faf751293d1331db39a + languageName: node + linkType: hard + +"tuf-js@npm:^2.2.1": + version: 2.2.1 + resolution: "tuf-js@npm:2.2.1" + dependencies: + "@tufjs/models": "npm:2.0.1" + debug: "npm:^4.3.4" + make-fetch-happen: "npm:^13.0.1" + checksum: 10c0/7c17b097571f001730d7be0aeaec6bec46ed2f25bf73990b1133c383d511a1ce65f831e5d6d78770940a85b67664576ff0e4c98e5421bab6d33ff36e4be500c8 + languageName: node + linkType: hard + +"type-fest@npm:^0.18.0": + version: 0.18.1 + resolution: "type-fest@npm:0.18.1" + checksum: 10c0/303f5ecf40d03e1d5b635ce7660de3b33c18ed8ebc65d64920c02974d9e684c72483c23f9084587e9dd6466a2ece1da42ddc95b412a461794dd30baca95e2bac + languageName: node + linkType: hard + +"type-fest@npm:^0.21.3": + version: 0.21.3 + resolution: "type-fest@npm:0.21.3" + checksum: 10c0/902bd57bfa30d51d4779b641c2bc403cdf1371fb9c91d3c058b0133694fcfdb817aef07a47f40faf79039eecbaa39ee9d3c532deff244f3a19ce68cea71a61e8 + languageName: node + linkType: hard + +"type-fest@npm:^0.4.1": + version: 0.4.1 + resolution: "type-fest@npm:0.4.1" + checksum: 10c0/2e65f43209492638244842f70d86e7325361c92dd1cc8e3bf5728c96b980305087fa5ba60652e9053d56c302ef4f1beb9652a91b72a50da0ea66c6b851f3b9cb + languageName: node + linkType: hard + +"type-fest@npm:^0.6.0": + version: 0.6.0 + resolution: "type-fest@npm:0.6.0" + checksum: 10c0/0c585c26416fce9ecb5691873a1301b5aff54673c7999b6f925691ed01f5b9232db408cdbb0bd003d19f5ae284322523f44092d1f81ca0a48f11f7cf0be8cd38 + languageName: node + linkType: hard + +"type-fest@npm:^0.8.1": + version: 0.8.1 + resolution: "type-fest@npm:0.8.1" + checksum: 10c0/dffbb99329da2aa840f506d376c863bd55f5636f4741ad6e65e82f5ce47e6914108f44f340a0b74009b0cb5d09d6752ae83203e53e98b1192cf80ecee5651636 + languageName: node + linkType: hard + +"type-is@npm:^1.6.4, type-is@npm:~1.6.18": + version: 1.6.18 + resolution: "type-is@npm:1.6.18" + dependencies: + media-typer: "npm:0.3.0" + mime-types: "npm:~2.1.24" + checksum: 10c0/a23daeb538591b7efbd61ecf06b6feb2501b683ffdc9a19c74ef5baba362b4347e42f1b4ed81f5882a8c96a3bfff7f93ce3ffaf0cbbc879b532b04c97a55db9d + languageName: node + linkType: hard + +"typedarray@npm:^0.0.6": + version: 0.0.6 + resolution: "typedarray@npm:0.0.6" + checksum: 10c0/6005cb31df50eef8b1f3c780eb71a17925f3038a100d82f9406ac2ad1de5eb59f8e6decbdc145b3a1f8e5836e17b0c0002fb698b9fe2516b8f9f9ff602d36412 + languageName: node + linkType: hard + +"typescript@npm:5.5.3, typescript@npm:>=3 < 6, typescript@npm:^5.5.3": + version: 5.5.3 + resolution: "typescript@npm:5.5.3" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/f52c71ccbc7080b034b9d3b72051d563601a4815bf3e39ded188e6ce60813f75dbedf11ad15dd4d32a12996a9ed8c7155b46c93a9b9c9bad1049766fe614bbdd + languageName: node + linkType: hard + +"typescript@patch:typescript@npm%3A5.5.3#optional!builtin, typescript@patch:typescript@npm%3A>=3 < 6#optional!builtin, typescript@patch:typescript@npm%3A^5.5.3#optional!builtin": + version: 5.5.3 + resolution: "typescript@patch:typescript@npm%3A5.5.3#optional!builtin::version=5.5.3&hash=b45daf" + bin: + tsc: bin/tsc + tsserver: bin/tsserver + checksum: 10c0/5a437c416251334deeaf29897157032311f3f126547cfdc4b133768b606cb0e62bcee733bb97cf74c42fe7268801aea1392d8e40988cdef112e9546eba4c03c5 + languageName: node + linkType: hard + +"uc.micro@npm:^2.0.0, uc.micro@npm:^2.1.0": + version: 2.1.0 + resolution: "uc.micro@npm:2.1.0" + checksum: 10c0/8862eddb412dda76f15db8ad1c640ccc2f47cdf8252a4a30be908d535602c8d33f9855dfcccb8b8837855c1ce1eaa563f7fa7ebe3c98fd0794351aab9b9c55fa + languageName: node + linkType: hard + +"uglify-js@npm:^3.1.4, uglify-js@npm:^3.5.1": + version: 3.19.0 + resolution: "uglify-js@npm:3.19.0" + bin: + uglifyjs: bin/uglifyjs + checksum: 10c0/c27d7a4734a59c5e2c08a6efd68bc534d559619f80ad437b1009ed56a7b1a8f6d6cbd5892a15879e0413d724e785b7227487ccca8d3e07261ba92d469c1447d3 + languageName: node + linkType: hard + +"undici-types@npm:~5.26.4": + version: 5.26.5 + resolution: "undici-types@npm:5.26.5" + checksum: 10c0/bb673d7876c2d411b6eb6c560e0c571eef4a01c1c19925175d16e3a30c4c428181fb8d7ae802a261f283e4166a0ac435e2f505743aa9e45d893f9a3df017b501 + languageName: node + linkType: hard + +"unicode-canonical-property-names-ecmascript@npm:^2.0.0": + version: 2.0.0 + resolution: "unicode-canonical-property-names-ecmascript@npm:2.0.0" + checksum: 10c0/0fe812641bcfa3ae433025178a64afb5d9afebc21a922dafa7cba971deebb5e4a37350423890750132a85c936c290fb988146d0b1bd86838ad4897f4fc5bd0de + languageName: node + linkType: hard + +"unicode-match-property-ecmascript@npm:^2.0.0": + version: 2.0.0 + resolution: "unicode-match-property-ecmascript@npm:2.0.0" + dependencies: + unicode-canonical-property-names-ecmascript: "npm:^2.0.0" + unicode-property-aliases-ecmascript: "npm:^2.0.0" + checksum: 10c0/4d05252cecaf5c8e36d78dc5332e03b334c6242faf7cf16b3658525441386c0a03b5f603d42cbec0f09bb63b9fd25c9b3b09667aee75463cac3efadae2cd17ec + languageName: node + linkType: hard + +"unicode-match-property-value-ecmascript@npm:^2.1.0": + version: 2.1.0 + resolution: "unicode-match-property-value-ecmascript@npm:2.1.0" + checksum: 10c0/f5b9499b9e0ffdc6027b744d528f17ec27dd7c15da03254ed06851feec47e0531f20d410910c8a49af4a6a190f4978413794c8d75ce112950b56d583b5d5c7f2 + languageName: node + linkType: hard + +"unicode-property-aliases-ecmascript@npm:^2.0.0": + version: 2.1.0 + resolution: "unicode-property-aliases-ecmascript@npm:2.1.0" + checksum: 10c0/50ded3f8c963c7785e48c510a3b7c6bc4e08a579551489aa0349680a35b1ceceec122e33b2b6c1b579d0be2250f34bb163ac35f5f8695fe10bbc67fb757f0af8 + languageName: node + linkType: hard + +"unique-filename@npm:^3.0.0": + version: 3.0.0 + resolution: "unique-filename@npm:3.0.0" + dependencies: + unique-slug: "npm:^4.0.0" + checksum: 10c0/6363e40b2fa758eb5ec5e21b3c7fb83e5da8dcfbd866cc0c199d5534c42f03b9ea9ab069769cc388e1d7ab93b4eeef28ef506ab5f18d910ef29617715101884f + languageName: node + linkType: hard + +"unique-slug@npm:^4.0.0": + version: 4.0.0 + resolution: "unique-slug@npm:4.0.0" + dependencies: + imurmurhash: "npm:^0.1.4" + checksum: 10c0/cb811d9d54eb5821b81b18205750be84cb015c20a4a44280794e915f5a0a70223ce39066781a354e872df3572e8155c228f43ff0cce94c7cbf4da2cc7cbdd635 + languageName: node + linkType: hard + +"universal-user-agent@npm:^6.0.0": + version: 6.0.1 + resolution: "universal-user-agent@npm:6.0.1" + checksum: 10c0/5c9c46ffe19a975e11e6443640ed4c9e0ce48fcc7203325757a8414ac49940ebb0f4667f2b1fa561489d1eb22cb2d05a0f7c82ec20c5cba42e58e188fb19b187 + languageName: node + linkType: hard + +"universalify@npm:^2.0.0": + version: 2.0.1 + resolution: "universalify@npm:2.0.1" + checksum: 10c0/73e8ee3809041ca8b818efb141801a1004e3fc0002727f1531f4de613ea281b494a40909596dae4a042a4fb6cd385af5d4db2e137b1362e0e91384b828effd3a + languageName: node + linkType: hard + +"unpipe@npm:1.0.0, unpipe@npm:~1.0.0": + version: 1.0.0 + resolution: "unpipe@npm:1.0.0" + checksum: 10c0/193400255bd48968e5c5383730344fbb4fa114cdedfab26e329e50dd2d81b134244bb8a72c6ac1b10ab0281a58b363d06405632c9d49ca9dfd5e90cbd7d0f32c + languageName: node + linkType: hard + +"upath@npm:2.0.1": + version: 2.0.1 + resolution: "upath@npm:2.0.1" + checksum: 10c0/79e8e1296b00e24a093b077cfd7a238712d09290c850ce59a7a01458ec78c8d26dcc2ab50b1b9d6a84dabf6511fb4969afeb8a5c9a001aa7272b9cc74c34670f + languageName: node + linkType: hard + +"update-browserslist-db@npm:^1.1.0": + version: 1.1.0 + resolution: "update-browserslist-db@npm:1.1.0" + dependencies: + escalade: "npm:^3.1.2" + picocolors: "npm:^1.0.1" + peerDependencies: + browserslist: ">= 4.21.0" + bin: + update-browserslist-db: cli.js + checksum: 10c0/a7452de47785842736fb71547651c5bbe5b4dc1e3722ccf48a704b7b34e4dcf633991eaa8e4a6a517ffb738b3252eede3773bef673ef9021baa26b056d63a5b9 + languageName: node + linkType: hard + +"upper-case@npm:^1.1.1": + version: 1.1.3 + resolution: "upper-case@npm:1.1.3" + checksum: 10c0/3e4d3a90519915bb591db84d72610392518806d8287b8f7541d87642d30388f42b2def1ed2f687e5792ee025e8f7e17d3a0dcbd5b3b59e306ceb1f3b8121ef54 + languageName: node + linkType: hard + +"use-sync-external-store@npm:^1.2.0, use-sync-external-store@npm:^1.2.2": + version: 1.2.2 + resolution: "use-sync-external-store@npm:1.2.2" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + checksum: 10c0/23b1597c10adf15b26ade9e8c318d8cc0abc9ec0ab5fc7ca7338da92e89c2536abd150a5891bf076836c352fdfa104fc7231fb48f806fd9960e0cbe03601abaf + languageName: node + linkType: hard + +"util-deprecate@npm:^1.0.1, util-deprecate@npm:^1.0.2, util-deprecate@npm:~1.0.1": + version: 1.0.2 + resolution: "util-deprecate@npm:1.0.2" + checksum: 10c0/41a5bdd214df2f6c3ecf8622745e4a366c4adced864bc3c833739791aeeeb1838119af7daed4ba36428114b5c67dcda034a79c882e97e43c03e66a4dd7389942 + languageName: node + linkType: hard + +"utils-merge@npm:1.0.1": + version: 1.0.1 + resolution: "utils-merge@npm:1.0.1" + checksum: 10c0/02ba649de1b7ca8854bfe20a82f1dfbdda3fb57a22ab4a8972a63a34553cf7aa51bc9081cf7e001b035b88186d23689d69e71b510e610a09a4c66f68aa95b672 + languageName: node + linkType: hard + +"uuid@npm:8.3.2": + version: 8.3.2 + resolution: "uuid@npm:8.3.2" + bin: + uuid: dist/bin/uuid + checksum: 10c0/bcbb807a917d374a49f475fae2e87fdca7da5e5530820ef53f65ba1d12131bd81a92ecf259cc7ce317cbe0f289e7d79fdfebcef9bfa3087c8c8a2fa304c9be54 + languageName: node + linkType: hard + +"uuid@npm:^10.0.0": + version: 10.0.0 + resolution: "uuid@npm:10.0.0" + bin: + uuid: dist/bin/uuid + checksum: 10c0/eab18c27fe4ab9fb9709a5d5f40119b45f2ec8314f8d4cf12ce27e4c6f4ffa4a6321dc7db6c515068fa373c075b49691ba969f0010bf37f44c37ca40cd6bf7fe + languageName: node + linkType: hard + +"uuid@npm:^9.0.1": + version: 9.0.1 + resolution: "uuid@npm:9.0.1" + bin: + uuid: dist/bin/uuid + checksum: 10c0/1607dd32ac7fc22f2d8f77051e6a64845c9bce5cd3dd8aa0070c074ec73e666a1f63c7b4e0f4bf2bc8b9d59dc85a15e17807446d9d2b17c8485fbc2147b27f9b + languageName: node + linkType: hard + +"v8-compile-cache-lib@npm:^3.0.1": + version: 3.0.1 + resolution: "v8-compile-cache-lib@npm:3.0.1" + checksum: 10c0/bdc36fb8095d3b41df197f5fb6f11e3a26adf4059df3213e3baa93810d8f0cc76f9a74aaefc18b73e91fe7e19154ed6f134eda6fded2e0f1c8d2272ed2d2d391 + languageName: node + linkType: hard + +"valid-data-url@npm:^3.0.0": + version: 3.0.1 + resolution: "valid-data-url@npm:3.0.1" + checksum: 10c0/ffc7cac681976ca2db01003dc14286f75241309e90d96e505580469125c83c2de6b5203f0222226cb08f6daf0aff7de9855655c28a64e8590e7b58c01694a896 + languageName: node + linkType: hard + +"validate-npm-package-license@npm:3.0.4, validate-npm-package-license@npm:^3.0.1, validate-npm-package-license@npm:^3.0.4": + version: 3.0.4 + resolution: "validate-npm-package-license@npm:3.0.4" + dependencies: + spdx-correct: "npm:^3.0.0" + spdx-expression-parse: "npm:^3.0.0" + checksum: 10c0/7b91e455a8de9a0beaa9fe961e536b677da7f48c9a493edf4d4d4a87fd80a7a10267d438723364e432c2fcd00b5650b5378275cded362383ef570276e6312f4f + languageName: node + linkType: hard + +"validate-npm-package-name@npm:5.0.1, validate-npm-package-name@npm:^5.0.0": + version: 5.0.1 + resolution: "validate-npm-package-name@npm:5.0.1" + checksum: 10c0/903e738f7387404bb72f7ac34e45d7010c877abd2803dc2d614612527927a40a6d024420033132e667b1bade94544b8a1f65c9431a4eb30d0ce0d80093cd1f74 + languageName: node + linkType: hard + +"vary@npm:^1, vary@npm:~1.1.2": + version: 1.1.2 + resolution: "vary@npm:1.1.2" + checksum: 10c0/f15d588d79f3675135ba783c91a4083dcd290a2a5be9fcb6514220a1634e23df116847b1cc51f66bfb0644cf9353b2abb7815ae499bab06e46dd33c1a6bf1f4f + languageName: node + linkType: hard + +"victory-vendor@npm:^36.6.8": + version: 36.9.2 + resolution: "victory-vendor@npm:36.9.2" + dependencies: + "@types/d3-array": "npm:^3.0.3" + "@types/d3-ease": "npm:^3.0.0" + "@types/d3-interpolate": "npm:^3.0.1" + "@types/d3-scale": "npm:^4.0.2" + "@types/d3-shape": "npm:^3.1.0" + "@types/d3-time": "npm:^3.0.0" + "@types/d3-timer": "npm:^3.0.0" + d3-array: "npm:^3.1.6" + d3-ease: "npm:^3.0.1" + d3-interpolate: "npm:^3.0.1" + d3-scale: "npm:^4.0.2" + d3-shape: "npm:^3.1.0" + d3-time: "npm:^3.0.0" + d3-timer: "npm:^3.0.1" + checksum: 10c0/bad36de3bf4d406834743c2e99a8281d786af324d7e84b7f7a2fc02c27a3779034fb0c3c4707d4c8e68683334d924a67100cfa13985235565e83b9877f8e2ffd + languageName: node + linkType: hard + +"w3c-keyname@npm:^2.2.0": + version: 2.2.8 + resolution: "w3c-keyname@npm:2.2.8" + checksum: 10c0/37cf335c90efff31672ebb345577d681e2177f7ff9006a9ad47c68c5a9d265ba4a7b39d6c2599ceea639ca9315584ce4bd9c9fbf7a7217bfb7a599e71943c4c4 + languageName: node + linkType: hard + +"walk-up-path@npm:^3.0.1": + version: 3.0.1 + resolution: "walk-up-path@npm:3.0.1" + checksum: 10c0/3184738e0cf33698dd58b0ee4418285b9c811e58698f52c1f025435a85c25cbc5a63fee599f1a79cb29ca7ef09a44ec9417b16bfd906b1a37c305f7aa20ee5bc + languageName: node + linkType: hard + +"wcwidth@npm:^1.0.0, wcwidth@npm:^1.0.1": + version: 1.0.1 + resolution: "wcwidth@npm:1.0.1" + dependencies: + defaults: "npm:^1.0.3" + checksum: 10c0/5b61ca583a95e2dd85d7078400190efd452e05751a64accb8c06ce4db65d7e0b0cde9917d705e826a2e05cc2548f61efde115ffa374c3e436d04be45c889e5b4 + languageName: node + linkType: hard + +"web-resource-inliner@npm:^6.0.1": + version: 6.0.1 + resolution: "web-resource-inliner@npm:6.0.1" + dependencies: + ansi-colors: "npm:^4.1.1" + escape-goat: "npm:^3.0.0" + htmlparser2: "npm:^5.0.0" + mime: "npm:^2.4.6" + node-fetch: "npm:^2.6.0" + valid-data-url: "npm:^3.0.0" + checksum: 10c0/b4b457de2448255100797b1eaefa0f62a8846b2452de8495b9ec17d3e223ebb4848a31b11a645e3541a5b114eb9f201219cda2f99d1b513631777f8c89d1c8a6 + languageName: node + linkType: hard + +"webidl-conversions@npm:^3.0.0": + version: 3.0.1 + resolution: "webidl-conversions@npm:3.0.1" + checksum: 10c0/5612d5f3e54760a797052eb4927f0ddc01383550f542ccd33d5238cfd65aeed392a45ad38364970d0a0f4fea32e1f4d231b3d8dac4a3bdd385e5cf802ae097db + languageName: node + linkType: hard + +"whatwg-url@npm:^5.0.0": + version: 5.0.0 + resolution: "whatwg-url@npm:5.0.0" + dependencies: + tr46: "npm:~0.0.3" + webidl-conversions: "npm:^3.0.0" + checksum: 10c0/1588bed84d10b72d5eec1d0faa0722ba1962f1821e7539c535558fb5398d223b0c50d8acab950b8c488b4ba69043fd833cc2697056b167d8ad46fac3995a55d5 + languageName: node + linkType: hard + +"which@npm:^2.0.1": + version: 2.0.2 + resolution: "which@npm:2.0.2" + dependencies: + isexe: "npm:^2.0.0" + bin: + node-which: ./bin/node-which + checksum: 10c0/66522872a768b60c2a65a57e8ad184e5372f5b6a9ca6d5f033d4b0dc98aff63995655a7503b9c0a2598936f532120e81dd8cc155e2e92ed662a2b9377cc4374f + languageName: node + linkType: hard + +"which@npm:^4.0.0": + version: 4.0.0 + resolution: "which@npm:4.0.0" + dependencies: + isexe: "npm:^3.1.1" + bin: + node-which: bin/which.js + checksum: 10c0/449fa5c44ed120ccecfe18c433296a4978a7583bf2391c50abce13f76878d2476defde04d0f79db8165bdf432853c1f8389d0485ca6e8ebce3bbcded513d5e6a + languageName: node + linkType: hard + +"wide-align@npm:1.1.5, wide-align@npm:^1.1.2": + version: 1.1.5 + resolution: "wide-align@npm:1.1.5" + dependencies: + string-width: "npm:^1.0.2 || 2 || 3 || 4" + checksum: 10c0/1d9c2a3e36dfb09832f38e2e699c367ef190f96b82c71f809bc0822c306f5379df87bab47bed27ea99106d86447e50eb972d3c516c2f95782807a9d082fbea95 + languageName: node + linkType: hard + +"wordwrap@npm:^1.0.0": + version: 1.0.0 + resolution: "wordwrap@npm:1.0.0" + checksum: 10c0/7ed2e44f3c33c5c3e3771134d2b0aee4314c9e49c749e37f464bf69f2bcdf0cbf9419ca638098e2717cff4875c47f56a007532f6111c3319f557a2ca91278e92 + languageName: node + linkType: hard + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0": + version: 7.0.0 + resolution: "wrap-ansi@npm:7.0.0" + dependencies: + ansi-styles: "npm:^4.0.0" + string-width: "npm:^4.1.0" + strip-ansi: "npm:^6.0.0" + checksum: 10c0/d15fc12c11e4cbc4044a552129ebc75ee3f57aa9c1958373a4db0292d72282f54373b536103987a4a7594db1ef6a4f10acf92978f79b98c49306a4b58c77d4da + languageName: node + linkType: hard + +"wrap-ansi@npm:^6.0.1": + version: 6.2.0 + resolution: "wrap-ansi@npm:6.2.0" + dependencies: + ansi-styles: "npm:^4.0.0" + string-width: "npm:^4.1.0" + strip-ansi: "npm:^6.0.0" + checksum: 10c0/baad244e6e33335ea24e86e51868fe6823626e3a3c88d9a6674642afff1d34d9a154c917e74af8d845fd25d170c4ea9cf69a47133c3f3656e1252b3d462d9f6c + languageName: node + linkType: hard + +"wrap-ansi@npm:^8.1.0": + version: 8.1.0 + resolution: "wrap-ansi@npm:8.1.0" + dependencies: + ansi-styles: "npm:^6.1.0" + string-width: "npm:^5.0.1" + strip-ansi: "npm:^7.0.1" + checksum: 10c0/138ff58a41d2f877eae87e3282c0630fc2789012fc1af4d6bd626eeb9a2f9a65ca92005e6e69a75c7b85a68479fe7443c7dbe1eb8fbaa681a4491364b7c55c60 + languageName: node + linkType: hard + +"wrappy@npm:1": + version: 1.0.2 + resolution: "wrappy@npm:1.0.2" + checksum: 10c0/56fece1a4018c6a6c8e28fbc88c87e0fbf4ea8fd64fc6c63b18f4acc4bd13e0ad2515189786dd2c30d3eec9663d70f4ecf699330002f8ccb547e4a18231fc9f0 + languageName: node + linkType: hard + +"write-file-atomic@npm:5.0.1, write-file-atomic@npm:^5.0.0": + version: 5.0.1 + resolution: "write-file-atomic@npm:5.0.1" + dependencies: + imurmurhash: "npm:^0.1.4" + signal-exit: "npm:^4.0.1" + checksum: 10c0/e8c850a8e3e74eeadadb8ad23c9d9d63e4e792bd10f4836ed74189ef6e996763959f1249c5650e232f3c77c11169d239cbfc8342fc70f3fe401407d23810505d + languageName: node + linkType: hard + +"write-file-atomic@npm:^2.4.2": + version: 2.4.3 + resolution: "write-file-atomic@npm:2.4.3" + dependencies: + graceful-fs: "npm:^4.1.11" + imurmurhash: "npm:^0.1.4" + signal-exit: "npm:^3.0.2" + checksum: 10c0/8cb4bba0c1ab814a9b127844da0db4fb8c5e06ddbe6317b8b319377c73b283673036c8b9360120062898508b9428d81611cf7fa97584504a00bc179b2a580b92 + languageName: node + linkType: hard + +"write-json-file@npm:^3.2.0": + version: 3.2.0 + resolution: "write-json-file@npm:3.2.0" + dependencies: + detect-indent: "npm:^5.0.0" + graceful-fs: "npm:^4.1.15" + make-dir: "npm:^2.1.0" + pify: "npm:^4.0.1" + sort-keys: "npm:^2.0.0" + write-file-atomic: "npm:^2.4.2" + checksum: 10c0/3eadcb6e832ac34dbba37d4eea8871d9fef0e0d77c486b13ed5f81d84a8fcecd9e1a04277e2691eb803c2bed39c2a315e98b96f492c271acee2836acc6276043 + languageName: node + linkType: hard + +"write-pkg@npm:4.0.0": + version: 4.0.0 + resolution: "write-pkg@npm:4.0.0" + dependencies: + sort-keys: "npm:^2.0.0" + type-fest: "npm:^0.4.1" + write-json-file: "npm:^3.2.0" + checksum: 10c0/8e20db5fa444dad04e3703c18d8e0f89679caa60accbee5da9ea3aa076430b3f32d99f50d8860d29044245775795455c62d12d16a7856d407e30df7b79f39505 + languageName: node + linkType: hard + +"xtend@npm:^4.0.0, xtend@npm:~4.0.1": + version: 4.0.2 + resolution: "xtend@npm:4.0.2" + checksum: 10c0/366ae4783eec6100f8a02dff02ac907bf29f9a00b82ac0264b4d8b832ead18306797e283cf19de776538babfdcb2101375ec5646b59f08c52128ac4ab812ed0e + languageName: node + linkType: hard + +"y18n@npm:^5.0.5": + version: 5.0.8 + resolution: "y18n@npm:5.0.8" + checksum: 10c0/4df2842c36e468590c3691c894bc9cdbac41f520566e76e24f59401ba7d8b4811eb1e34524d57e54bc6d864bcb66baab7ffd9ca42bf1eda596618f9162b91249 + languageName: node + linkType: hard + +"yallist@npm:^3.0.2": + version: 3.1.1 + resolution: "yallist@npm:3.1.1" + checksum: 10c0/c66a5c46bc89af1625476f7f0f2ec3653c1a1791d2f9407cfb4c2ba812a1e1c9941416d71ba9719876530e3340a99925f697142989371b72d93b9ee628afd8c1 + languageName: node + linkType: hard + +"yallist@npm:^4.0.0": + version: 4.0.0 + resolution: "yallist@npm:4.0.0" + checksum: 10c0/2286b5e8dbfe22204ab66e2ef5cc9bbb1e55dfc873bbe0d568aa943eb255d131890dfd5bf243637273d31119b870f49c18fcde2c6ffbb7a7a092b870dc90625a + languageName: node + linkType: hard + +"yaml@npm:^2.3.4": + version: 2.4.5 + resolution: "yaml@npm:2.4.5" + bin: + yaml: bin.mjs + checksum: 10c0/e1ee78b381e5c710f715cc4082fd10fc82f7f5c92bd6f075771d20559e175616f56abf1c411f545ea0e9e16e4f84a83a50b42764af5f16ec006328ba9476bb31 + languageName: node + linkType: hard + +"yargs-parser@npm:21.1.1, yargs-parser@npm:^21.1.1": + version: 21.1.1 + resolution: "yargs-parser@npm:21.1.1" + checksum: 10c0/f84b5e48169479d2f402239c59f084cfd1c3acc197a05c59b98bab067452e6b3ea46d4dd8ba2985ba7b3d32a343d77df0debd6b343e5dae3da2aab2cdf5886b2 + languageName: node + linkType: hard + +"yargs-parser@npm:^20.2.2, yargs-parser@npm:^20.2.3": + version: 20.2.9 + resolution: "yargs-parser@npm:20.2.9" + checksum: 10c0/0685a8e58bbfb57fab6aefe03c6da904a59769bd803a722bb098bd5b0f29d274a1357762c7258fb487512811b8063fb5d2824a3415a0a4540598335b3b086c72 + languageName: node + linkType: hard + +"yargs@npm:17.7.2, yargs@npm:^17.6.2, yargs@npm:^17.7.2": + version: 17.7.2 + resolution: "yargs@npm:17.7.2" + dependencies: + cliui: "npm:^8.0.1" + escalade: "npm:^3.1.1" + get-caller-file: "npm:^2.0.5" + require-directory: "npm:^2.1.1" + string-width: "npm:^4.2.3" + y18n: "npm:^5.0.5" + yargs-parser: "npm:^21.1.1" + checksum: 10c0/ccd7e723e61ad5965fffbb791366db689572b80cca80e0f96aad968dfff4156cd7cd1ad18607afe1046d8241e6fb2d6c08bf7fa7bfb5eaec818735d8feac8f05 + languageName: node + linkType: hard + +"yargs@npm:^16.2.0": + version: 16.2.0 + resolution: "yargs@npm:16.2.0" + dependencies: + cliui: "npm:^7.0.2" + escalade: "npm:^3.1.1" + get-caller-file: "npm:^2.0.5" + require-directory: "npm:^2.1.1" + string-width: "npm:^4.2.0" + y18n: "npm:^5.0.5" + yargs-parser: "npm:^20.2.2" + checksum: 10c0/b1dbfefa679848442454b60053a6c95d62f2d2e21dd28def92b647587f415969173c6e99a0f3bab4f1b67ee8283bf735ebe3544013f09491186ba9e8a9a2b651 + languageName: node + linkType: hard + +"yn@npm:3.1.1": + version: 3.1.1 + resolution: "yn@npm:3.1.1" + checksum: 10c0/0732468dd7622ed8a274f640f191f3eaf1f39d5349a1b72836df484998d7d9807fbea094e2f5486d6b0cd2414aad5775972df0e68f8604db89a239f0f4bf7443 + languageName: node + linkType: hard + +"zod@npm:^3.23.8": + version: 3.23.8 + resolution: "zod@npm:3.23.8" + checksum: 10c0/8f14c87d6b1b53c944c25ce7a28616896319d95bc46a9660fe441adc0ed0a81253b02b5abdaeffedbeb23bdd25a0bf1c29d2c12dd919aef6447652dd295e3e69 + languageName: node + linkType: hard