From 91a8840c3cec332a12b615a34c927f86ddf42a57 Mon Sep 17 00:00:00 2001 From: Dries Augustyns Date: Tue, 23 Jul 2024 13:49:48 +0200 Subject: [PATCH] Initial Commit --- .dockerignore | 7 + .github/workflows/docker-build.yml | 31 + .gitignore | 12 + .yarnrc.yml | 1 + Dockerfile | 35 + README.md | 28 + assets/card.png | Bin 0 -> 25154 bytes biome.json | 21 + deployment/entry.sh | 18 + deployment/nginx.conf | 25 + deployment/replace-variables.sh | 16 + lerna.json | 7 + package.json | 38 + packages/api/.env.example | 13 + packages/api/package.json | 51 + packages/api/src/app.ts | 108 + packages/api/src/app/constants.ts | 42 + packages/api/src/app/cron.ts | 15 + packages/api/src/controllers/Auth.ts | 125 + packages/api/src/controllers/Identities.ts | 171 + packages/api/src/controllers/Memberships.ts | 147 + packages/api/src/controllers/Projects.ts | 624 + packages/api/src/controllers/Tasks.ts | 140 + packages/api/src/controllers/Users.ts | 32 + .../src/controllers/Webhooks/Incoming/SNS.ts | 191 + .../controllers/Webhooks/Incoming/index.ts | 6 + .../api/src/controllers/Webhooks/index.ts | 6 + packages/api/src/controllers/v1/Actions.ts | 270 + packages/api/src/controllers/v1/Campaigns.ts | 347 + packages/api/src/controllers/v1/Contacts.ts | 334 + packages/api/src/controllers/v1/Events.ts | 39 + packages/api/src/controllers/v1/Templates.ts | 253 + packages/api/src/controllers/v1/index.ts | 298 + packages/api/src/database/prisma.ts | 3 + packages/api/src/exceptions/index.ts | 34 + packages/api/src/middleware/auth.ts | 177 + packages/api/src/services/ActionService.ts | 185 + packages/api/src/services/AuthService.ts | 18 + packages/api/src/services/CampaignService.ts | 17 + packages/api/src/services/ContactService.ts | 24 + packages/api/src/services/EmailService.ts | 531 + packages/api/src/services/EventService.ts | 25 + .../api/src/services/MembershipService.ts | 76 + packages/api/src/services/ProjectService.ts | 468 + packages/api/src/services/TemplateService.ts | 17 + packages/api/src/services/UserService.ts | 61 + packages/api/src/services/keys.ts | 140 + packages/api/src/services/redis.ts | 27 + packages/api/src/util/hash.ts | 33 + packages/api/src/util/ses.ts | 53 + packages/api/src/util/tokens.ts | 9 + packages/api/tsconfig.json | 11 + packages/dashboard/.env.example | 2 + packages/dashboard/next-env.d.ts | 5 + packages/dashboard/next.config.js | 18 + packages/dashboard/package.json | 70 + packages/dashboard/postcss.config.js | 6 + packages/dashboard/public/assets/card.png | Bin 0 -> 21917 bytes packages/dashboard/public/assets/logo.png | Bin 0 -> 27266 bytes packages/dashboard/public/assets/shared.svg | 158 + .../public/favicon/android-chrome-192x192.png | Bin 0 -> 3211 bytes .../public/favicon/android-chrome-512x512.png | Bin 0 -> 9339 bytes .../public/favicon/apple-touch-icon.png | Bin 0 -> 3136 bytes .../public/favicon/browserconfig.xml | 9 + .../public/favicon/favicon-16x16.png | Bin 0 -> 658 bytes .../public/favicon/favicon-32x32.png | Bin 0 -> 1064 bytes packages/dashboard/public/favicon/favicon.ico | Bin 0 -> 15086 bytes .../public/favicon/mstile-150x150.png | Bin 0 -> 3751 bytes .../public/favicon/safari-pinned-tab.svg | 73 + .../dashboard/public/favicon/site.webmanifest | 19 + .../dashboard/src/components/Alert/Alert.tsx | 32 + .../dashboard/src/components/Alert/index.tsx | 1 + .../dashboard/src/components/Badge/Badge.tsx | 26 + .../dashboard/src/components/Badge/index.tsx | 1 + .../dashboard/src/components/Card/Card.tsx | 101 + .../dashboard/src/components/Card/index.tsx | 1 + .../src/components/CodeBlock/CodeBlock.tsx | 118 + .../src/components/CodeBlock/index.tsx | 1 + .../components/Input/Dropdown/Dropdown.tsx | 191 + .../src/components/Input/Dropdown/index.tsx | 1 + .../src/components/Input/Input/Input.tsx | 57 + .../src/components/Input/Input/index.ts | 1 + .../Input/MarkdownEditor/Editor.tsx | 873 + .../MarkdownEditor/extensions/Button.tsx | 109 + .../extensions/ColorSelector.tsx | 98 + .../extensions/EditorBubbleMenu.tsx | 96 + .../MetadataSuggestion/MetadataSuggestion.tsx | 151 + .../MetadataSuggestion/SuggestionList.tsx | 79 + .../MetadataSuggestion/Suggestions.ts | 87 + .../extensions/NodeSelector.tsx | 100 + .../MarkdownEditor/extensions/Progress.tsx | 133 + .../Input/MarkdownEditor/extensions/Slash.tsx | 350 + .../components/Input/MarkdownEditor/index.tsx | 1 + .../MultiselectDropdown.tsx | 181 + .../Input/MultiselectDropdown/index.tsx | 1 + .../src/components/Input/Toggle/Toggle.tsx | 54 + .../src/components/Input/Toggle/index.tsx | 1 + .../dashboard/src/components/Input/index.tsx | 5 + .../AnalyticsTabs/AnalyticsTabs.tsx | 19 + .../Navigation/AnalyticsTabs/index.tsx | 1 + .../DeveloperTabs/DeveloperTabs.tsx | 14 + .../Navigation/DeveloperTabs/index.tsx | 1 + .../ProjectSelector/ProjectSelector.tsx | 159 + .../Navigation/ProjectSelector/index.tsx | 1 + .../Navigation/SettingTabs/SettingTabs.tsx | 19 + .../Navigation/SettingTabs/index.tsx | 1 + .../components/Navigation/Sidebar/Sidebar.tsx | 366 + .../components/Navigation/Sidebar/index.tsx | 1 + .../src/components/Navigation/Tabs/Tabs.tsx | 61 + .../src/components/Navigation/Tabs/index.tsx | 1 + .../src/components/Navigation/index.tsx | 6 + .../src/components/Overlay/Modal/Modal.tsx | 181 + .../src/components/Overlay/Modal/index.tsx | 1 + .../src/components/Overlay/index.tsx | 1 + .../src/components/Skeleton/Skeleton.tsx | 81 + .../src/components/Skeleton/index.tsx | 1 + .../dashboard/src/components/Table/Table.tsx | 110 + .../dashboard/src/components/Table/index.tsx | 1 + .../src/components/Utility/Empty/Empty.tsx | 37 + .../src/components/Utility/Empty/index.tsx | 1 + .../FullscreenLoader/FullscreenLoader.tsx | 26 + .../Utility/FullscreenLoader/index.tsx | 1 + .../Utility/ProgressBar/ProgressBar.tsx | 24 + .../components/Utility/ProgressBar/index.tsx | 1 + .../components/Utility/Redirect/Redirect.tsx | 20 + .../src/components/Utility/Redirect/index.tsx | 1 + .../components/Utility/Tooltip/Tooltip.tsx | 37 + .../src/components/Utility/Tooltip/index.tsx | 1 + .../src/components/Utility/index.tsx | 5 + packages/dashboard/src/components/index.ts | 10 + packages/dashboard/src/layouts/Dashboard.tsx | 72 + packages/dashboard/src/layouts/index.ts | 1 + packages/dashboard/src/lib/atoms/project.ts | 5 + packages/dashboard/src/lib/constants.ts | 4 + packages/dashboard/src/lib/hooks/actions.ts | 43 + packages/dashboard/src/lib/hooks/analytics.ts | 34 + packages/dashboard/src/lib/hooks/campaigns.ts | 33 + packages/dashboard/src/lib/hooks/contacts.ts | 106 + packages/dashboard/src/lib/hooks/emails.ts | 21 + packages/dashboard/src/lib/hooks/events.ts | 29 + packages/dashboard/src/lib/hooks/projects.ts | 82 + packages/dashboard/src/lib/hooks/templates.ts | 24 + packages/dashboard/src/lib/hooks/users.ts | 9 + packages/dashboard/src/lib/network.ts | 67 + packages/dashboard/src/pages/_app.tsx | 105 + packages/dashboard/src/pages/_document.tsx | 37 + packages/dashboard/src/pages/actions/[id].tsx | 614 + .../dashboard/src/pages/actions/index.tsx | 303 + packages/dashboard/src/pages/actions/new.tsx | 389 + .../dashboard/src/pages/analytics/clicks.tsx | 95 + .../dashboard/src/pages/analytics/index.tsx | 555 + packages/dashboard/src/pages/auth/login.tsx | 263 + packages/dashboard/src/pages/auth/logout.tsx | 29 + packages/dashboard/src/pages/auth/reset.tsx | 188 + packages/dashboard/src/pages/auth/signup.tsx | 266 + .../dashboard/src/pages/campaigns/[id].tsx | 1098 ++ .../dashboard/src/pages/campaigns/index.tsx | 257 + .../dashboard/src/pages/campaigns/new.tsx | 738 + .../dashboard/src/pages/contacts/[id].tsx | 777 + .../dashboard/src/pages/contacts/index.tsx | 540 + packages/dashboard/src/pages/events/index.tsx | 432 + packages/dashboard/src/pages/index.tsx | 300 + packages/dashboard/src/pages/manage/[id].tsx | 138 + packages/dashboard/src/pages/new.tsx | 264 + .../src/pages/onboarding/actions.tsx | 1057 ++ .../dashboard/src/pages/onboarding/index.tsx | 76 + .../src/pages/onboarding/transactional.tsx | 242 + .../dashboard/src/pages/settings/account.tsx | 42 + packages/dashboard/src/pages/settings/api.tsx | 143 + .../dashboard/src/pages/settings/identity.tsx | 356 + .../dashboard/src/pages/settings/index.tsx | 8 + .../dashboard/src/pages/settings/members.tsx | 234 + .../dashboard/src/pages/settings/project.tsx | 197 + .../dashboard/src/pages/subscribe/[id].tsx | 179 + .../dashboard/src/pages/templates/[id].tsx | 355 + .../dashboard/src/pages/templates/index.tsx | 165 + .../dashboard/src/pages/templates/new.tsx | 245 + .../dashboard/src/pages/unsubscribe/[id].tsx | 180 + packages/dashboard/styles/index.css | 58 + packages/dashboard/tailwind.config.js | 17 + packages/dashboard/tsconfig.json | 24 + packages/shared/package.json | 20 + packages/shared/src/index.ts | 264 + packages/shared/tsconfig.json | 13 + prisma/.env.example | 1 + .../20240719085125_init/migration.sql | 330 + prisma/migrations/migration_lock.toml | 3 + prisma/schema.prisma | 329 + tools/preinstall.js | 10 + tsconfig.json | 32 + yarn.lock | 13764 ++++++++++++++++ 191 files changed, 36022 insertions(+) create mode 100644 .dockerignore create mode 100644 .github/workflows/docker-build.yml create mode 100644 .gitignore create mode 100644 .yarnrc.yml create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 assets/card.png create mode 100644 biome.json create mode 100644 deployment/entry.sh create mode 100644 deployment/nginx.conf create mode 100644 deployment/replace-variables.sh create mode 100644 lerna.json create mode 100644 package.json create mode 100644 packages/api/.env.example create mode 100644 packages/api/package.json create mode 100644 packages/api/src/app.ts create mode 100644 packages/api/src/app/constants.ts create mode 100644 packages/api/src/app/cron.ts create mode 100644 packages/api/src/controllers/Auth.ts create mode 100644 packages/api/src/controllers/Identities.ts create mode 100644 packages/api/src/controllers/Memberships.ts create mode 100644 packages/api/src/controllers/Projects.ts create mode 100644 packages/api/src/controllers/Tasks.ts create mode 100644 packages/api/src/controllers/Users.ts create mode 100644 packages/api/src/controllers/Webhooks/Incoming/SNS.ts create mode 100644 packages/api/src/controllers/Webhooks/Incoming/index.ts create mode 100644 packages/api/src/controllers/Webhooks/index.ts create mode 100644 packages/api/src/controllers/v1/Actions.ts create mode 100644 packages/api/src/controllers/v1/Campaigns.ts create mode 100644 packages/api/src/controllers/v1/Contacts.ts create mode 100644 packages/api/src/controllers/v1/Events.ts create mode 100644 packages/api/src/controllers/v1/Templates.ts create mode 100644 packages/api/src/controllers/v1/index.ts create mode 100644 packages/api/src/database/prisma.ts create mode 100644 packages/api/src/exceptions/index.ts create mode 100644 packages/api/src/middleware/auth.ts create mode 100644 packages/api/src/services/ActionService.ts create mode 100644 packages/api/src/services/AuthService.ts create mode 100644 packages/api/src/services/CampaignService.ts create mode 100644 packages/api/src/services/ContactService.ts create mode 100644 packages/api/src/services/EmailService.ts create mode 100644 packages/api/src/services/EventService.ts create mode 100644 packages/api/src/services/MembershipService.ts create mode 100644 packages/api/src/services/ProjectService.ts create mode 100644 packages/api/src/services/TemplateService.ts create mode 100644 packages/api/src/services/UserService.ts create mode 100644 packages/api/src/services/keys.ts create mode 100644 packages/api/src/services/redis.ts create mode 100644 packages/api/src/util/hash.ts create mode 100644 packages/api/src/util/ses.ts create mode 100644 packages/api/src/util/tokens.ts create mode 100644 packages/api/tsconfig.json create mode 100644 packages/dashboard/.env.example create mode 100644 packages/dashboard/next-env.d.ts create mode 100644 packages/dashboard/next.config.js create mode 100644 packages/dashboard/package.json create mode 100644 packages/dashboard/postcss.config.js create mode 100644 packages/dashboard/public/assets/card.png create mode 100644 packages/dashboard/public/assets/logo.png create mode 100644 packages/dashboard/public/assets/shared.svg create mode 100644 packages/dashboard/public/favicon/android-chrome-192x192.png create mode 100644 packages/dashboard/public/favicon/android-chrome-512x512.png create mode 100644 packages/dashboard/public/favicon/apple-touch-icon.png create mode 100644 packages/dashboard/public/favicon/browserconfig.xml create mode 100644 packages/dashboard/public/favicon/favicon-16x16.png create mode 100644 packages/dashboard/public/favicon/favicon-32x32.png create mode 100644 packages/dashboard/public/favicon/favicon.ico create mode 100644 packages/dashboard/public/favicon/mstile-150x150.png create mode 100644 packages/dashboard/public/favicon/safari-pinned-tab.svg create mode 100644 packages/dashboard/public/favicon/site.webmanifest create mode 100644 packages/dashboard/src/components/Alert/Alert.tsx create mode 100644 packages/dashboard/src/components/Alert/index.tsx create mode 100644 packages/dashboard/src/components/Badge/Badge.tsx create mode 100644 packages/dashboard/src/components/Badge/index.tsx create mode 100644 packages/dashboard/src/components/Card/Card.tsx create mode 100644 packages/dashboard/src/components/Card/index.tsx create mode 100644 packages/dashboard/src/components/CodeBlock/CodeBlock.tsx create mode 100644 packages/dashboard/src/components/CodeBlock/index.tsx create mode 100644 packages/dashboard/src/components/Input/Dropdown/Dropdown.tsx create mode 100644 packages/dashboard/src/components/Input/Dropdown/index.tsx create mode 100644 packages/dashboard/src/components/Input/Input/Input.tsx create mode 100644 packages/dashboard/src/components/Input/Input/index.ts create mode 100644 packages/dashboard/src/components/Input/MarkdownEditor/Editor.tsx create mode 100644 packages/dashboard/src/components/Input/MarkdownEditor/extensions/Button.tsx create mode 100644 packages/dashboard/src/components/Input/MarkdownEditor/extensions/ColorSelector.tsx create mode 100644 packages/dashboard/src/components/Input/MarkdownEditor/extensions/EditorBubbleMenu.tsx create mode 100644 packages/dashboard/src/components/Input/MarkdownEditor/extensions/MetadataSuggestion/MetadataSuggestion.tsx create mode 100644 packages/dashboard/src/components/Input/MarkdownEditor/extensions/MetadataSuggestion/SuggestionList.tsx create mode 100644 packages/dashboard/src/components/Input/MarkdownEditor/extensions/MetadataSuggestion/Suggestions.ts create mode 100644 packages/dashboard/src/components/Input/MarkdownEditor/extensions/NodeSelector.tsx create mode 100644 packages/dashboard/src/components/Input/MarkdownEditor/extensions/Progress.tsx create mode 100644 packages/dashboard/src/components/Input/MarkdownEditor/extensions/Slash.tsx create mode 100644 packages/dashboard/src/components/Input/MarkdownEditor/index.tsx create mode 100644 packages/dashboard/src/components/Input/MultiselectDropdown/MultiselectDropdown.tsx create mode 100644 packages/dashboard/src/components/Input/MultiselectDropdown/index.tsx create mode 100644 packages/dashboard/src/components/Input/Toggle/Toggle.tsx create mode 100644 packages/dashboard/src/components/Input/Toggle/index.tsx create mode 100644 packages/dashboard/src/components/Input/index.tsx create mode 100644 packages/dashboard/src/components/Navigation/AnalyticsTabs/AnalyticsTabs.tsx create mode 100644 packages/dashboard/src/components/Navigation/AnalyticsTabs/index.tsx create mode 100644 packages/dashboard/src/components/Navigation/DeveloperTabs/DeveloperTabs.tsx create mode 100644 packages/dashboard/src/components/Navigation/DeveloperTabs/index.tsx create mode 100644 packages/dashboard/src/components/Navigation/ProjectSelector/ProjectSelector.tsx create mode 100644 packages/dashboard/src/components/Navigation/ProjectSelector/index.tsx create mode 100644 packages/dashboard/src/components/Navigation/SettingTabs/SettingTabs.tsx create mode 100644 packages/dashboard/src/components/Navigation/SettingTabs/index.tsx create mode 100644 packages/dashboard/src/components/Navigation/Sidebar/Sidebar.tsx create mode 100644 packages/dashboard/src/components/Navigation/Sidebar/index.tsx create mode 100644 packages/dashboard/src/components/Navigation/Tabs/Tabs.tsx create mode 100644 packages/dashboard/src/components/Navigation/Tabs/index.tsx create mode 100644 packages/dashboard/src/components/Navigation/index.tsx create mode 100644 packages/dashboard/src/components/Overlay/Modal/Modal.tsx create mode 100644 packages/dashboard/src/components/Overlay/Modal/index.tsx create mode 100644 packages/dashboard/src/components/Overlay/index.tsx create mode 100644 packages/dashboard/src/components/Skeleton/Skeleton.tsx create mode 100644 packages/dashboard/src/components/Skeleton/index.tsx create mode 100644 packages/dashboard/src/components/Table/Table.tsx create mode 100644 packages/dashboard/src/components/Table/index.tsx create mode 100644 packages/dashboard/src/components/Utility/Empty/Empty.tsx create mode 100644 packages/dashboard/src/components/Utility/Empty/index.tsx create mode 100644 packages/dashboard/src/components/Utility/FullscreenLoader/FullscreenLoader.tsx create mode 100644 packages/dashboard/src/components/Utility/FullscreenLoader/index.tsx create mode 100644 packages/dashboard/src/components/Utility/ProgressBar/ProgressBar.tsx create mode 100644 packages/dashboard/src/components/Utility/ProgressBar/index.tsx create mode 100644 packages/dashboard/src/components/Utility/Redirect/Redirect.tsx create mode 100644 packages/dashboard/src/components/Utility/Redirect/index.tsx create mode 100644 packages/dashboard/src/components/Utility/Tooltip/Tooltip.tsx create mode 100644 packages/dashboard/src/components/Utility/Tooltip/index.tsx create mode 100644 packages/dashboard/src/components/Utility/index.tsx create mode 100644 packages/dashboard/src/components/index.ts create mode 100644 packages/dashboard/src/layouts/Dashboard.tsx create mode 100644 packages/dashboard/src/layouts/index.ts create mode 100644 packages/dashboard/src/lib/atoms/project.ts create mode 100644 packages/dashboard/src/lib/constants.ts create mode 100644 packages/dashboard/src/lib/hooks/actions.ts create mode 100644 packages/dashboard/src/lib/hooks/analytics.ts create mode 100644 packages/dashboard/src/lib/hooks/campaigns.ts create mode 100644 packages/dashboard/src/lib/hooks/contacts.ts create mode 100644 packages/dashboard/src/lib/hooks/emails.ts create mode 100644 packages/dashboard/src/lib/hooks/events.ts create mode 100644 packages/dashboard/src/lib/hooks/projects.ts create mode 100644 packages/dashboard/src/lib/hooks/templates.ts create mode 100644 packages/dashboard/src/lib/hooks/users.ts create mode 100644 packages/dashboard/src/lib/network.ts create mode 100644 packages/dashboard/src/pages/_app.tsx create mode 100644 packages/dashboard/src/pages/_document.tsx create mode 100644 packages/dashboard/src/pages/actions/[id].tsx create mode 100644 packages/dashboard/src/pages/actions/index.tsx create mode 100644 packages/dashboard/src/pages/actions/new.tsx create mode 100644 packages/dashboard/src/pages/analytics/clicks.tsx create mode 100644 packages/dashboard/src/pages/analytics/index.tsx create mode 100644 packages/dashboard/src/pages/auth/login.tsx create mode 100644 packages/dashboard/src/pages/auth/logout.tsx create mode 100644 packages/dashboard/src/pages/auth/reset.tsx create mode 100644 packages/dashboard/src/pages/auth/signup.tsx create mode 100644 packages/dashboard/src/pages/campaigns/[id].tsx create mode 100644 packages/dashboard/src/pages/campaigns/index.tsx create mode 100644 packages/dashboard/src/pages/campaigns/new.tsx create mode 100644 packages/dashboard/src/pages/contacts/[id].tsx create mode 100644 packages/dashboard/src/pages/contacts/index.tsx create mode 100644 packages/dashboard/src/pages/events/index.tsx create mode 100644 packages/dashboard/src/pages/index.tsx create mode 100644 packages/dashboard/src/pages/manage/[id].tsx create mode 100644 packages/dashboard/src/pages/new.tsx create mode 100644 packages/dashboard/src/pages/onboarding/actions.tsx create mode 100644 packages/dashboard/src/pages/onboarding/index.tsx create mode 100644 packages/dashboard/src/pages/onboarding/transactional.tsx create mode 100644 packages/dashboard/src/pages/settings/account.tsx create mode 100644 packages/dashboard/src/pages/settings/api.tsx create mode 100644 packages/dashboard/src/pages/settings/identity.tsx create mode 100644 packages/dashboard/src/pages/settings/index.tsx create mode 100644 packages/dashboard/src/pages/settings/members.tsx create mode 100644 packages/dashboard/src/pages/settings/project.tsx create mode 100644 packages/dashboard/src/pages/subscribe/[id].tsx create mode 100644 packages/dashboard/src/pages/templates/[id].tsx create mode 100644 packages/dashboard/src/pages/templates/index.tsx create mode 100644 packages/dashboard/src/pages/templates/new.tsx create mode 100644 packages/dashboard/src/pages/unsubscribe/[id].tsx create mode 100644 packages/dashboard/styles/index.css create mode 100644 packages/dashboard/tailwind.config.js create mode 100644 packages/dashboard/tsconfig.json create mode 100644 packages/shared/package.json create mode 100644 packages/shared/src/index.ts create mode 100644 packages/shared/tsconfig.json create mode 100644 prisma/.env.example create mode 100644 prisma/migrations/20240719085125_init/migration.sql create mode 100644 prisma/migrations/migration_lock.toml create mode 100644 prisma/schema.prisma create mode 100644 tools/preinstall.js create mode 100644 tsconfig.json create mode 100644 yarn.lock 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 0000000000000000000000000000000000000000..7b8f7c94b50da769efae89bc12a3a9f3b13649e6 GIT binary patch literal 25154 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�FCC-fwosr$Eu6R^%+UQWpIHnZ) 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 JwGPPd{}Yd3ue$&M literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..5a1c256f304f4bb970c5344b19d616dc9533b99b GIT binary patch literal 21917 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 literal 0 HcmV?d00001 diff --git a/packages/dashboard/public/assets/logo.png b/packages/dashboard/public/assets/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..0022f5a9060ec2dfac876ae862ee5299bfd15e9d GIT binary patch literal 27266 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ 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 0000000000000000000000000000000000000000..4f87bc34dbc655961458899b39d51e0150fb1911 GIT binary patch literal 3211 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..6026be49d08c9580e5b95dbadbaa01e61c937f77 GIT binary patch literal 3136 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 Y09N_9G200BEcyz-6lrBxhj5SkAExcu4gdfE literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..1f715d61bd7dc01db6319d1f0fa9da753b0557e9 GIT binary patch literal 658 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6SkfJR9T^xl_H+M9WCijSl0AZa z85pY67#JE_7#My5g&JNkFq9fFFuY1&V6d9Oz#v{QXIG#NP=YDR+ueoXe|!I#{XiaP zfk$L90|U1(2s1Lwnj--eWH0gbb!C6f$i=H}lJ;=!Sq271B~KT}5RLP>7wo-fCrY$_ zod0mGg~~!hg%?U4O0g{5eA^T(yxdF@#F&qzbLM(Se>^^ z_&7ri)c*OJ-a4+>VsbTm@41KjEU$P-Y<=c(awe~`)YXfrktbW{*J*HcEjyaE>+m$| z?q5rm?%k|;;^oR$YVkI+rW>XDNj2-SKE8T@T~)I1ub$|aMZtas&O8$XC$e6@=V4La z7Ur4SKGk9N_qv~5c_~ki)(UNZAN};ef@*ImmoQez!(mfWc|+|bgwH5G-TmNR^Q2N{ zDFr94$}Jh9DLJ<{K3{dI?*kWq#`QHB_x|nweYBwa`io01uDt)%m~8Rs#aa*ZK2e*+ zXB&$rm$A;@d!j`;wf^tNk53&YN4D$fc>G&Gk74`Ia2b8Zuv5SQR4s9hC`m~yNwrEY zN(E93Mg~U4x(0^2h6W*q##Y9bR;EVU1_o9J2LIp2Pe;*^o1c=IR*73f--hSfKn>wl zArU1(iRB6fMfqu&IjIUIl?AB^nFS@u3=9=>9)IHDC=AokIOTu(jOWuJ24-b$y<~1- zWnu5hBFw@HE)6D!Q<#-EhbWxBaplC3Ge=~Ou%B-5Sm33{@Jd{;RG<|Mp00i_ I>zopr0IMGJ?EnA( literal 0 HcmV?d00001 diff --git a/packages/dashboard/public/favicon/favicon-32x32.png b/packages/dashboard/public/favicon/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..0f6f0eb2cc9007486484657a31ea7d554a2d23d4 GIT binary patch literal 1064 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 literal 0 HcmV?d00001 diff --git a/packages/dashboard/public/favicon/mstile-150x150.png b/packages/dashboard/public/favicon/mstile-150x150.png new file mode 100644 index 0000000000000000000000000000000000000000..472b468189307c3d28e3e235969e4f58d6f6f213 GIT binary patch literal 3751 zcmcInS5Omb(~cq_RRxtIRTNN)Kp=#E5RhI&37{ywNGFs)K$H-~K!k%dsRE%(6KQe~ z2ptki5Ger(0V35yk$~jjmvi}F{5Rjsw=+9CyU#xR?Ci{Y@g`eY7;>=-vI77BE@LBo z8vx)O_rHGe0;4k3rYO${7rb@Nbpe3J*Bo?LW=1aNZe(K)07Ody0P#ow;1{D5{~Z7b zR{#Jup8x>rIRF4(aDIoiCIdL{W@@Mp_?`56>@y={g&CV0uu{*lb4e+Ckqht)Q8QzG zUE8Snoy8b``=7#tdy&q?*pFDb00Aq%QS5iyK4~5UMeC$IufUFXy51@1gC;Y4)AxMW z$$ry(uRNkZ8Vp?QOcUT!S<+?Bf3dRSDK<5Ned7F%c@)bR4e!vo%Z)rE(3+_m8$aOM z^;FS6!=ekr;;1+{;iTXjv;Kc!Hg*q48Va1L#qn+<(xq?Q2GUGHj<>#-6lfuIc^Bp$ z0OeoTB90?a91w;Yq+SiO)}pVb38L&ptkU3oukq{h%Pbjk6P zW@HZwcgf5Yap3efNuPIFt?+_KUz3k7%Ob%|xrDhfcS)?c-d^}AbR4aTH>^5?WQYue zuvvacz3^9h?Axdm;4tQN!edz}X9VAA>5XhW{#}u1sOFpYRXo{%sg-DgI$g`_K zMOr6#Vw9sdJn!jXJ}I!=3>R%NX-kk{In-&%06F<$69UE1{tq$?<<$}@=d0bdYbf%6 zHrS{LtJECJHJys@fsVBf2`|$;{x%oEYT0VW2}hF4S{r3SF00wWjLqmRlca9UW%4m{q>V#5 zc5Od?W5-xnRo^x!clf-vYLZ0H&k#QAj*-;|N6dxAA}%XsMnzNdo)u~$qlb03Mfe0Q zY-DIQJbN^+ebX@9yGQvVk&4(-0R&H!-`h6OoojO^nx0(|B*92sY|+g^4o$ftMIB!} z&C5E7bFd}_^QmFVl!ar18+^z4IM9Z+OdAOHcQqmPXSxvFV!H2CQ4!SZO44qU)DX7p zeJu;u^N!*)a$k4s_tWMu3r3rT30z_sx&7oh=8(^_-*xQE@ogtgx*Y=cuago|`ky11Wh%Wo!_7X9Os;+pEQwU{PgmN{9l=EsoeStc5~$^cpbbYzu?$c7%!LN{(=GD z--dMUHAaSoq9xViP!ec#!Ot5qP{ymgt0yzX4MNV=D81r(`Y~zVtCr4bsN$`W6|rx$ z9L`kDl{x#kL8Z^d9Q5&0z*MG0dzW6j`^KrPqCYNFW1c@464O8taD~>O2FkL`kqA!1 zxIgmho(WbrhE;(OkODG$^JDH0W?S#Mt;2YLqBz9_ULn)1sHKKfH%Wn+YV1O%gWZ@y z?U$ZBWBG!MC;XT&ehRb$Y00%9T`|@$^XqbqO7Y$93~&I>#VC0Vcb%>`2T* z52M&?K%iXO-Pasu^+XpM6@wcL7Q!oHu%ZVTUzwKbu+tACck=z9PcMy2w!?0&JqBAu z&RBLn$Z);kSiUg%fY<^EZJSb-Cf!{!kJ!^6ZVmPTer;^VA717-DescVOW6TF5upmq zY_2FCTu);D)@!|skgQZ$+ReCD+)NzMu)=a3PSP+z)ur9$V; zEtM_@f&SF;(^LzQGWbOk>HA%Y?k$u@{+C8mopb$s;I1+D!^`Y-O4=2gz^lw}x*lj4 zo>qHzglIfdXueWkI?{kerxb`q2HTSovo|F_%!QoK5*(xndJ3v+?{rgN%gSRXXGeDm zCt`zcm%$0==66AX*c25M@g%C-GEhYIt>Gb@2xHRRhAv5Piw>%zqP8NAtkfpJL|GFR z!rPe*+0~owt%)qxe-VFe{h^&Gk>DRDDzcxfQrIoBgz$W?Th?%^FWanx^ z-lgPuBgumR`IWxQi^aT}V~b}5cHG#*Z`_detSKLM7QhfWJp#E zsien@1>Ea$^;-ufIq+LHx{4&?wwBNB{vFHCRn$w~JnBpc7ex05mGRX&Y~_BpdnMhitsd@uf9l_CQLg z9|PiN(^d_{jvdzW*xxWckqG*w)chh-hs?j#mcFgr+NiFyKa6vB?75jWA?VO^>$MJ9 zI->dIcLfKKIT~h?Dw(ysQQ&(!u@vU8)un1yf`pt&_vybCfF=mh(4dV#GTjK2q zHp8rgM}FBGEPEG;F_~Q2t&6X+?vaOJXMl*jUh7_coYNMB>=~oXXhaDusxw=gxppqo$EhuH%TFT03(%KG_#RHqA>lKs_ z=CYmebe7Sg;5K>M3F@qd$hp;Q#YcP2Eyb8`7nJ)WKNkBMn`>FxF6Za;uYh+2_Cj+K?M2z{<@$`{>0GJRE z$E{mnrk&_sbC4K)ok~0KlN(h&J@h)Lbo0ek1UENLwIc#`Ac$+ZE*)RWLZ6*H-@7B! zK$^?zLYi_fOf`=jx)|UQ+++vv(xs@9W3p?#@CQyjVjkjop7LX*9F%1qwXv#*Hiiu&Q>Cb`p%Zo z5nP$Ljub*X?>{0Rx|~iuT5@_ro|2MoTT6S|Emzg82})Xb`_?KOl%h{UM6>iMcRQFY zBc(ppmJfqtC?M)~$V$4+l2RdTiz=t5(Wtyi34rp_3`xErnN9^2n1_Wg|L&M1z(YjS z^f#hF`qwd+%rY*c4eQF}N`grOw0B0+p>0L_7bbmHk!_a@N*qQ$TtQOCQREwadUlHL^vnNV+X2mc4qkY~LI?WK%TzEs0U;eMnXrF!w zk&ZV{M~mfW zv6q$}u^b&UUb6u(NpQ%!bMQ*=nr2*O;uX`6SH^iTNqn#s8=ajV>Kp45>SG;^H!3!f Z2W;|Pf$iT{$z(_Xj14UG>+U~!@gE8PFwX!0 literal 0 HcmV?d00001 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