From dcb3e19c75809bb844a992dc12b24efd3bb7691e Mon Sep 17 00:00:00 2001 From: tiniscule <114822071+tiniscule@users.noreply.github.com> Date: Sun, 3 Dec 2023 16:43:03 -0700 Subject: [PATCH] Headless Basejump migration (#31) --- .env.sample | 5 - .eslintrc.json | 7 - .gitattributes | 2 - .github/workflows/dbdev.yml | 28 + .github/workflows/pg_tests.yml | 16 - .github/workflows/tests.yml | 108 +- .gitignore | 8 +- .vscode/settings.json | 10 - README.md | 82 +- __tests__/content/blog/en/article-1.md | 6 - __tests__/content/blog/en/article-2.md | 9 - __tests__/content/blog/en/unpublished-blog.md | 7 - __tests__/content/docs/en/doc-2.md | 7 - __tests__/content/docs/en/index.md | 10 - __tests__/content/docs/en/unpublished-doc.md | 6 - __tests__/core/input.test.tsx | 28 - __tests__/core/select.test.tsx | 28 - .../settings/update-team-member-role.test.tsx | 75 - .../dashboard/profile/update-email.test.tsx | 74 - .../dashboard/profile/update-name.test.tsx | 75 - __tests__/setup/jest.setup.js | 20 - __tests__/setup/test-utils.tsx | 33 - __tests__/utils/content-helpers.test.ts | 23 - .../utils/handle-supabase-errors.test.ts | 13 - __tests__/utils/slug-to-title.test.ts | 15 - basejump_core--2.0.0.sql | 1355 ++++ basejump_core.control | 4 + content/blog/en/formatting-example.md | 90 - content/blog/en/hello-world.md | 10 - content/blog/en/unpublished-blog.md | 7 - content/docs/en/formatting-example.md | 90 - content/docs/en/index.md | 10 - content/docs/en/unpublished-doc.md | 6 - content/locales/en/authentication.json | 21 - content/locales/en/content.json | 10 - content/locales/en/dashboard.json | 156 - i18n.js | 23 - jest.config.js | 48 - next.config.js | 17 - package.json | 60 - postcss.config.js | 6 - public/images/basejump-logo.png | Bin 16809 -> 0 bytes public/images/placeholder-image.svg | 720 -- public/images/vercel-logo.svg | 6 - public/robots.txt | 3 - scripts/install-dbdev-with-test-helpers.sql | 51 + scripts/setup.sh | 9 + scripts/sync-stripe.ts | 35 - .../future-content-placeholder.tsx | 33 - .../basejump-default-content/homepage.tsx | 231 - .../basejump-default-content/logo.tsx | 49 - .../content-pages/content-footer.tsx | 13 - .../content-pages/content-header-mobile.tsx | 74 - .../content-pages/content-header.tsx | 68 - .../content-pages/content-layout.tsx | 29 - src/components/content-pages/content-meta.tsx | 45 - src/components/core/input.tsx | 49 - src/components/core/loader.tsx | 96 - src/components/core/portal.tsx | 12 - src/components/core/select.tsx | 72 - .../account-subscription-takeover.tsx | 46 - .../individual-subscription-plan.tsx | 79 - .../new-subscription.tsx | 52 - .../dashboard/accounts/new-account-form.tsx | 71 - .../dashboard/accounts/new-account-modal.tsx | 27 - .../accounts/personal-account-deactivated.tsx | 37 - .../settings/account-settings-layout.tsx | 58 - .../settings/account-subscription.tsx | 75 - .../settings/individual-team-invitation.tsx | 88 - .../settings/individual-team-member.tsx | 71 - .../accounts/settings/invite-member.tsx | 137 - .../settings/list-team-invitations.tsx | 32 - .../accounts/settings/list-team-members.tsx | 32 - .../accounts/settings/remove-team-member.tsx | 48 - .../accounts/settings/update-account-name.tsx | 77 - .../settings/update-team-member-role.tsx | 90 - .../authentication/login-magic-link.tsx | 78 - .../authentication/login-password.tsx | 65 - .../authentication/signup-password.tsx | 79 - src/components/dashboard/dashboard-layout.tsx | 76 - src/components/dashboard/dashboard-meta.tsx | 14 - .../dashboard/profile/list-teams.tsx | 75 - .../profile/update-email-address.tsx | 65 - .../dashboard/profile/update-profile-name.tsx | 73 - .../dashboard/shared/dashboard-content.tsx | 49 - .../dashboard/shared/settings-card.tsx | 47 - .../sidebar/personal-account-menu.tsx | 51 - .../dashboard/sidebar/profile-button.tsx | 51 - .../dashboard/sidebar/sidebar-menu.tsx | 55 - .../dashboard/sidebar/team-account-menu.tsx | 49 - .../dashboard/sidebar/team-select-menu.tsx | 98 - .../dashboard/sidebar/theme-selector.tsx | 67 - src/components/docs/docs-layout.tsx | 48 - src/components/docs/docs-sidebar.tsx | 64 - src/middleware.ts | 31 - src/pages/_app.tsx | 56 - src/pages/api/billing/portal-link.ts | 59 - src/pages/api/billing/setup.ts | 62 - src/pages/api/billing/status.ts | 85 - src/pages/api/billing/stripe-webhooks.ts | 113 - src/pages/api/og.tsx | 47 - src/pages/blog/[...slug].tsx | 80 - src/pages/blog/index.tsx | 57 - src/pages/dashboard/billing.tsx | 21 - src/pages/dashboard/index.tsx | 39 - src/pages/dashboard/profile.tsx | 25 - .../dashboard/teams/[accountId]/index.tsx | 22 - .../teams/[accountId]/settings/billing.tsx | 23 - .../teams/[accountId]/settings/index.tsx | 23 - .../teams/[accountId]/settings/members.tsx | 42 - src/pages/docs/[...slug].tsx | 77 - src/pages/docs/index.tsx | 45 - src/pages/index.tsx | 5 - src/pages/invitation.tsx | 58 - src/pages/login.tsx | 24 - src/pages/signup.tsx | 23 - src/styles/global.css | 3 - src/types/auth.ts | 8 - src/types/billing.ts | 1 - src/types/supabase-types.ts | 468 -- src/utils/admin/stripe-billing-helpers.ts | 288 - src/utils/admin/stripe.ts | 12 - src/utils/admin/supabase-admin-client.ts | 7 - src/utils/api/use-account-billing-options.ts | 60 - src/utils/api/use-account-billing-status.ts | 44 - src/utils/api/use-dashboard-overview.ts | 50 - src/utils/api/use-invitation.ts | 32 - src/utils/api/use-personal-account.ts | 28 - src/utils/api/use-team-account.ts | 27 - src/utils/api/use-team-accounts.ts | 36 - src/utils/api/use-team-invitations.ts | 31 - src/utils/api/use-team-members.ts | 42 - src/utils/api/use-team-role.ts | 45 - src/utils/api/use-user-profile.ts | 24 - src/utils/content/content-helpers.ts | 155 - src/utils/content/slug-to-title.ts | 7 - src/utils/content/use-header-navigation.ts | 15 - src/utils/get-full-redirect-url.ts | 5 - src/utils/get-invitation-url.ts | 4 - src/utils/handle-supabase-errors.ts | 5 - src/utils/use-auth-check.ts | 23 - src/utils/use-theme-storage.ts | 22 - supabase/.gitignore | 4 +- supabase/config.toml | 82 - supabase/functions/.env.example | 4 + .../deno-packages/billing-functions/README.md | 175 + .../deno-packages/billing-functions/deps.ts | 2 + .../billing-functions/lib/cors-headers.ts | 5 + .../lib/create-supabase-client.ts | 13 + .../lib/create-supabase-service-client.ts | 10 + .../billing-functions/lib/error-response.ts | 16 + .../billing-functions/lib/upsert-data.ts | 55 + .../billing-functions/lib/validate-url.ts | 10 + .../deno-packages/billing-functions/mod.ts | 17 + .../src/billing-functions-wrapper.ts | 207 + .../src/billing-webhooks-wrapper.ts | 41 + .../find-or-create-customer.ts | 54 + .../find-or-create-subscription.ts | 66 + .../stripe/billing-functions/get-plans.ts | 17 + .../stripe/billing-functions/stripe-utils.ts | 53 + .../stripe/stripe-function-handler.ts | 114 + .../stripe/stripe-webhook-handler.ts | 89 + .../src/require-authorized-billing-user.ts | 93 + .../src/require-authorized-user.ts | 62 + .../src/wrappers/get-billing-status.ts | 43 + .../types/basejump-database.ts | 330 + .../test-stripe-billing-functions/index.ts | 31 + .../test-stripe-billing-webhooks/index.ts | 29 + .../00000000000000_dbdev_temp_install.sql | 37 - .../00000000000001_utility_functions.sql | 132 - .../migrations/00000000000002_accounts.sql | 293 - .../00000000000003_billing_setup.sql | 200 - .../migrations/00000000000010_profiles.sql | 115 - .../migrations/00000000000011_invitations.sql | 166 - supabase/seed.sql | 12 - ...tests.sql => 01-basejump-schema-tests.sql} | 33 +- ...-accounts.sql => 02-personal-accounts.sql} | 69 +- ...team-accounts.sql => 04-team-accounts.sql} | 130 +- ...bled.sql => 05-team-accounts-disabled.sql} | 5 +- .../{6-invitations.sql => 06-invitations.sql} | 149 +- ...member.sql => 07-inviting-team-member.sql} | 38 +- ...m-owner.sql => 08-inviting-team-owner.sql} | 28 +- .../database/09-removing-team-members.sql | 86 + supabase/tests/database/10-account-roles.sql | 45 +- .../database/11-public-account-functions.sql | 252 + .../tests/database/12-created-by-tracking.sql | 73 + .../database/14-public-billing-functions.sql | 199 + .../15-billing-disabled-functions.sql | 115 + .../database/3-personal-accounts-disabled.sql | 23 - supabase/tests/database/9-profiles.sql | 73 - supabase/tests/integration/.gitignore | 5 + .../integration/invalid-requests.spec.ts | 59 + supabase/tests/integration/package.json | 20 + .../tests/integration/playwright.config.ts | 77 + .../stripe-full-no-webhooks.spec.ts | 134 + .../stripe-full-only-webhooks.spec.ts | 118 + .../utils/get-verified-account-status.ts | 22 + .../utils/get-verified-billing-portal-url.ts | 19 + .../get-verified-new-subscription-url.ts | 21 + .../utils/setup-basejump-account.ts | 29 + .../tests/integration/utils/stripe-actions.ts | 156 + .../utils/test-invalid-billing-portal-url.ts | 28 + .../test-invalid-new-subscription-url.ts | 39 + .../utils/test-missing-account-id.ts | 22 + .../integration/utils/test-unauthorized.ts | 41 + supabase/tests/integration/utils/variables.ts | 3 + supabase/tests/integration/yarn.lock | 354 + tailwind.config.js | 73 - tsconfig.json | 39 - yarn.lock | 6230 ----------------- 210 files changed, 5311 insertions(+), 14859 deletions(-) delete mode 100644 .env.sample delete mode 100644 .eslintrc.json delete mode 100644 .gitattributes create mode 100644 .github/workflows/dbdev.yml delete mode 100644 .github/workflows/pg_tests.yml delete mode 100644 .vscode/settings.json delete mode 100644 __tests__/content/blog/en/article-1.md delete mode 100644 __tests__/content/blog/en/article-2.md delete mode 100644 __tests__/content/blog/en/unpublished-blog.md delete mode 100644 __tests__/content/docs/en/doc-2.md delete mode 100644 __tests__/content/docs/en/index.md delete mode 100644 __tests__/content/docs/en/unpublished-doc.md delete mode 100644 __tests__/core/input.test.tsx delete mode 100644 __tests__/core/select.test.tsx delete mode 100644 __tests__/dashboard/accounts/settings/update-team-member-role.test.tsx delete mode 100644 __tests__/dashboard/profile/update-email.test.tsx delete mode 100644 __tests__/dashboard/profile/update-name.test.tsx delete mode 100644 __tests__/setup/jest.setup.js delete mode 100644 __tests__/setup/test-utils.tsx delete mode 100644 __tests__/utils/content-helpers.test.ts delete mode 100644 __tests__/utils/handle-supabase-errors.test.ts delete mode 100644 __tests__/utils/slug-to-title.test.ts create mode 100644 basejump_core--2.0.0.sql create mode 100644 basejump_core.control delete mode 100644 content/blog/en/formatting-example.md delete mode 100644 content/blog/en/hello-world.md delete mode 100644 content/blog/en/unpublished-blog.md delete mode 100644 content/docs/en/formatting-example.md delete mode 100644 content/docs/en/index.md delete mode 100644 content/docs/en/unpublished-doc.md delete mode 100644 content/locales/en/authentication.json delete mode 100644 content/locales/en/content.json delete mode 100644 content/locales/en/dashboard.json delete mode 100644 i18n.js delete mode 100644 jest.config.js delete mode 100644 next.config.js delete mode 100644 package.json delete mode 100644 postcss.config.js delete mode 100644 public/images/basejump-logo.png delete mode 100644 public/images/placeholder-image.svg delete mode 100644 public/images/vercel-logo.svg delete mode 100644 public/robots.txt create mode 100644 scripts/install-dbdev-with-test-helpers.sql create mode 100755 scripts/setup.sh delete mode 100644 scripts/sync-stripe.ts delete mode 100644 src/components/basejump-default-content/future-content-placeholder.tsx delete mode 100644 src/components/basejump-default-content/homepage.tsx delete mode 100644 src/components/basejump-default-content/logo.tsx delete mode 100644 src/components/content-pages/content-footer.tsx delete mode 100644 src/components/content-pages/content-header-mobile.tsx delete mode 100644 src/components/content-pages/content-header.tsx delete mode 100644 src/components/content-pages/content-layout.tsx delete mode 100644 src/components/content-pages/content-meta.tsx delete mode 100644 src/components/core/input.tsx delete mode 100644 src/components/core/loader.tsx delete mode 100644 src/components/core/portal.tsx delete mode 100644 src/components/core/select.tsx delete mode 100644 src/components/dashboard/accounts/account-subscription-takeover/account-subscription-takeover.tsx delete mode 100644 src/components/dashboard/accounts/account-subscription-takeover/individual-subscription-plan.tsx delete mode 100644 src/components/dashboard/accounts/account-subscription-takeover/new-subscription.tsx delete mode 100644 src/components/dashboard/accounts/new-account-form.tsx delete mode 100644 src/components/dashboard/accounts/new-account-modal.tsx delete mode 100644 src/components/dashboard/accounts/personal-account-deactivated.tsx delete mode 100644 src/components/dashboard/accounts/settings/account-settings-layout.tsx delete mode 100644 src/components/dashboard/accounts/settings/account-subscription.tsx delete mode 100644 src/components/dashboard/accounts/settings/individual-team-invitation.tsx delete mode 100644 src/components/dashboard/accounts/settings/individual-team-member.tsx delete mode 100644 src/components/dashboard/accounts/settings/invite-member.tsx delete mode 100644 src/components/dashboard/accounts/settings/list-team-invitations.tsx delete mode 100644 src/components/dashboard/accounts/settings/list-team-members.tsx delete mode 100644 src/components/dashboard/accounts/settings/remove-team-member.tsx delete mode 100644 src/components/dashboard/accounts/settings/update-account-name.tsx delete mode 100644 src/components/dashboard/accounts/settings/update-team-member-role.tsx delete mode 100644 src/components/dashboard/authentication/login-magic-link.tsx delete mode 100644 src/components/dashboard/authentication/login-password.tsx delete mode 100644 src/components/dashboard/authentication/signup-password.tsx delete mode 100644 src/components/dashboard/dashboard-layout.tsx delete mode 100644 src/components/dashboard/dashboard-meta.tsx delete mode 100644 src/components/dashboard/profile/list-teams.tsx delete mode 100644 src/components/dashboard/profile/update-email-address.tsx delete mode 100644 src/components/dashboard/profile/update-profile-name.tsx delete mode 100644 src/components/dashboard/shared/dashboard-content.tsx delete mode 100644 src/components/dashboard/shared/settings-card.tsx delete mode 100644 src/components/dashboard/sidebar/personal-account-menu.tsx delete mode 100644 src/components/dashboard/sidebar/profile-button.tsx delete mode 100644 src/components/dashboard/sidebar/sidebar-menu.tsx delete mode 100644 src/components/dashboard/sidebar/team-account-menu.tsx delete mode 100644 src/components/dashboard/sidebar/team-select-menu.tsx delete mode 100644 src/components/dashboard/sidebar/theme-selector.tsx delete mode 100644 src/components/docs/docs-layout.tsx delete mode 100644 src/components/docs/docs-sidebar.tsx delete mode 100644 src/middleware.ts delete mode 100644 src/pages/_app.tsx delete mode 100644 src/pages/api/billing/portal-link.ts delete mode 100644 src/pages/api/billing/setup.ts delete mode 100644 src/pages/api/billing/status.ts delete mode 100644 src/pages/api/billing/stripe-webhooks.ts delete mode 100644 src/pages/api/og.tsx delete mode 100644 src/pages/blog/[...slug].tsx delete mode 100644 src/pages/blog/index.tsx delete mode 100644 src/pages/dashboard/billing.tsx delete mode 100644 src/pages/dashboard/index.tsx delete mode 100644 src/pages/dashboard/profile.tsx delete mode 100644 src/pages/dashboard/teams/[accountId]/index.tsx delete mode 100644 src/pages/dashboard/teams/[accountId]/settings/billing.tsx delete mode 100644 src/pages/dashboard/teams/[accountId]/settings/index.tsx delete mode 100644 src/pages/dashboard/teams/[accountId]/settings/members.tsx delete mode 100644 src/pages/docs/[...slug].tsx delete mode 100644 src/pages/docs/index.tsx delete mode 100644 src/pages/index.tsx delete mode 100644 src/pages/invitation.tsx delete mode 100644 src/pages/login.tsx delete mode 100644 src/pages/signup.tsx delete mode 100644 src/styles/global.css delete mode 100644 src/types/auth.ts delete mode 100644 src/types/billing.ts delete mode 100644 src/types/supabase-types.ts delete mode 100644 src/utils/admin/stripe-billing-helpers.ts delete mode 100644 src/utils/admin/stripe.ts delete mode 100644 src/utils/admin/supabase-admin-client.ts delete mode 100644 src/utils/api/use-account-billing-options.ts delete mode 100644 src/utils/api/use-account-billing-status.ts delete mode 100644 src/utils/api/use-dashboard-overview.ts delete mode 100644 src/utils/api/use-invitation.ts delete mode 100644 src/utils/api/use-personal-account.ts delete mode 100644 src/utils/api/use-team-account.ts delete mode 100644 src/utils/api/use-team-accounts.ts delete mode 100644 src/utils/api/use-team-invitations.ts delete mode 100644 src/utils/api/use-team-members.ts delete mode 100644 src/utils/api/use-team-role.ts delete mode 100644 src/utils/api/use-user-profile.ts delete mode 100644 src/utils/content/content-helpers.ts delete mode 100644 src/utils/content/slug-to-title.ts delete mode 100644 src/utils/content/use-header-navigation.ts delete mode 100644 src/utils/get-full-redirect-url.ts delete mode 100644 src/utils/get-invitation-url.ts delete mode 100644 src/utils/handle-supabase-errors.ts delete mode 100644 src/utils/use-auth-check.ts delete mode 100644 src/utils/use-theme-storage.ts delete mode 100644 supabase/config.toml create mode 100644 supabase/functions/.env.example create mode 100644 supabase/functions/deno-packages/billing-functions/README.md create mode 100644 supabase/functions/deno-packages/billing-functions/deps.ts create mode 100644 supabase/functions/deno-packages/billing-functions/lib/cors-headers.ts create mode 100644 supabase/functions/deno-packages/billing-functions/lib/create-supabase-client.ts create mode 100644 supabase/functions/deno-packages/billing-functions/lib/create-supabase-service-client.ts create mode 100644 supabase/functions/deno-packages/billing-functions/lib/error-response.ts create mode 100644 supabase/functions/deno-packages/billing-functions/lib/upsert-data.ts create mode 100644 supabase/functions/deno-packages/billing-functions/lib/validate-url.ts create mode 100644 supabase/functions/deno-packages/billing-functions/mod.ts create mode 100644 supabase/functions/deno-packages/billing-functions/src/billing-functions-wrapper.ts create mode 100644 supabase/functions/deno-packages/billing-functions/src/billing-webhooks-wrapper.ts create mode 100644 supabase/functions/deno-packages/billing-functions/src/providers/stripe/billing-functions/find-or-create-customer.ts create mode 100644 supabase/functions/deno-packages/billing-functions/src/providers/stripe/billing-functions/find-or-create-subscription.ts create mode 100644 supabase/functions/deno-packages/billing-functions/src/providers/stripe/billing-functions/get-plans.ts create mode 100644 supabase/functions/deno-packages/billing-functions/src/providers/stripe/billing-functions/stripe-utils.ts create mode 100644 supabase/functions/deno-packages/billing-functions/src/providers/stripe/stripe-function-handler.ts create mode 100644 supabase/functions/deno-packages/billing-functions/src/providers/stripe/stripe-webhook-handler.ts create mode 100644 supabase/functions/deno-packages/billing-functions/src/require-authorized-billing-user.ts create mode 100644 supabase/functions/deno-packages/billing-functions/src/require-authorized-user.ts create mode 100644 supabase/functions/deno-packages/billing-functions/src/wrappers/get-billing-status.ts create mode 100644 supabase/functions/deno-packages/billing-functions/types/basejump-database.ts create mode 100644 supabase/functions/test-stripe-billing-functions/index.ts create mode 100644 supabase/functions/test-stripe-billing-webhooks/index.ts delete mode 100644 supabase/migrations/00000000000000_dbdev_temp_install.sql delete mode 100644 supabase/migrations/00000000000001_utility_functions.sql delete mode 100644 supabase/migrations/00000000000002_accounts.sql delete mode 100644 supabase/migrations/00000000000003_billing_setup.sql delete mode 100644 supabase/migrations/00000000000010_profiles.sql delete mode 100644 supabase/migrations/00000000000011_invitations.sql delete mode 100644 supabase/seed.sql rename supabase/tests/database/{1-basejump-schema-tests.sql => 01-basejump-schema-tests.sql} (58%) rename supabase/tests/database/{2-personal-accounts.sql => 02-personal-accounts.sql} (52%) rename supabase/tests/database/{4-team-accounts.sql => 04-team-accounts.sql} (57%) rename supabase/tests/database/{5-team-accounts-disabled.sql => 05-team-accounts-disabled.sql} (75%) rename supabase/tests/database/{6-invitations.sql => 06-invitations.sql} (50%) rename supabase/tests/database/{7-inviting-team-member.sql => 07-inviting-team-member.sql} (59%) rename supabase/tests/database/{8-inviting-team-owner.sql => 08-inviting-team-owner.sql} (67%) create mode 100644 supabase/tests/database/09-removing-team-members.sql create mode 100644 supabase/tests/database/11-public-account-functions.sql create mode 100644 supabase/tests/database/12-created-by-tracking.sql create mode 100644 supabase/tests/database/14-public-billing-functions.sql create mode 100644 supabase/tests/database/15-billing-disabled-functions.sql delete mode 100644 supabase/tests/database/3-personal-accounts-disabled.sql delete mode 100644 supabase/tests/database/9-profiles.sql create mode 100644 supabase/tests/integration/.gitignore create mode 100644 supabase/tests/integration/invalid-requests.spec.ts create mode 100644 supabase/tests/integration/package.json create mode 100644 supabase/tests/integration/playwright.config.ts create mode 100644 supabase/tests/integration/stripe-full-no-webhooks.spec.ts create mode 100644 supabase/tests/integration/stripe-full-only-webhooks.spec.ts create mode 100644 supabase/tests/integration/utils/get-verified-account-status.ts create mode 100644 supabase/tests/integration/utils/get-verified-billing-portal-url.ts create mode 100644 supabase/tests/integration/utils/get-verified-new-subscription-url.ts create mode 100644 supabase/tests/integration/utils/setup-basejump-account.ts create mode 100644 supabase/tests/integration/utils/stripe-actions.ts create mode 100644 supabase/tests/integration/utils/test-invalid-billing-portal-url.ts create mode 100644 supabase/tests/integration/utils/test-invalid-new-subscription-url.ts create mode 100644 supabase/tests/integration/utils/test-missing-account-id.ts create mode 100644 supabase/tests/integration/utils/test-unauthorized.ts create mode 100644 supabase/tests/integration/utils/variables.ts create mode 100644 supabase/tests/integration/yarn.lock delete mode 100644 tailwind.config.js delete mode 100644 tsconfig.json delete mode 100644 yarn.lock diff --git a/.env.sample b/.env.sample deleted file mode 100644 index fff7b8c..0000000 --- a/.env.sample +++ /dev/null @@ -1,5 +0,0 @@ -URL="http://localhost:3000" -NEXT_PUBLIC_SUPABASE_ANON_KEY=generated-by-supabase -NEXT_PUBLIC_SUPABASE_URL=generated-by-supabase -STRIPE_SECRET_KEY=generated-by-stripe -SUPABASE_SERVICE_ROLE_KEY=generated-by-supabase diff --git a/.eslintrc.json b/.eslintrc.json deleted file mode 100644 index 3bf5171..0000000 --- a/.eslintrc.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "extends": [ - "next", - "next/core-web-vitals", - "prettier" - ] -} diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index dfe0770..0000000 --- a/.gitattributes +++ /dev/null @@ -1,2 +0,0 @@ -# Auto detect text files and perform LF normalization -* text=auto diff --git a/.github/workflows/dbdev.yml b/.github/workflows/dbdev.yml new file mode 100644 index 0000000..f195ce3 --- /dev/null +++ b/.github/workflows/dbdev.yml @@ -0,0 +1,28 @@ +name: Build DBDev +on: + workflow_call: +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + - name: Cache dbdev + id: cache-dbdev + uses: actions/cache@v2 + with: + path: ~/.cargo/bin/dbdev + key: ${{ runner.os }}-dbdev + + - name: Install dbdev + uses: actions-rs/cargo@v1 + with: + command: install + args: --git https://github.com/supabase/dbdev.git dbdev --force + if: steps.cache-dbdev.outputs.cache-hit != 'true' + - name: Create build-output artifact + uses: actions/upload-artifact@master + with: + name: dbdev-output + path: ~/.cargo/bin/dbdev \ No newline at end of file diff --git a/.github/workflows/pg_tests.yml b/.github/workflows/pg_tests.yml deleted file mode 100644 index e2ece1c..0000000 --- a/.github/workflows/pg_tests.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: PGTap Tests -on: - pull_request: - branches: [ main ] -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: supabase/setup-cli@v1 - with: - version: 1.50.11 - - name: Supabase Start - run: supabase start - - name: Run Tests - run: supabase db test \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f4e0a1d..285eba1 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,15 +1,105 @@ -name: Jest Tests and Linting +name: Tests on: pull_request: branches: [ main ] jobs: - build: + dbdev: + uses: ./.github/workflows/dbdev.yml + pg_tap_tests: + name: PGTap Tests runs-on: ubuntu-latest + needs: dbdev steps: - - uses: actions/checkout@v2 - - name: Install modules - run: yarn install --frozen-lockfile - - name: Next Linting - run: yarn lint - - name: Run tests - run: yarn test \ No newline at end of file + - name: Download DBDev + uses: actions/download-artifact@master + with: + name: dbdev-output + path: ~/.cargo/bin + - name: Make dbdev executable + run: chmod +x ~/.cargo/bin/dbdev + - uses: actions/checkout@v3 + - uses: supabase/setup-cli@v1 + with: + version: latest + - name: Supabase Start + run: supabase init && supabase start + - name: Install supabase_test_helpers from scripts/install-dbdev-with-test-helpers.sql (remove this once remote install enabled in dbdev CLI) + run: psql -v ON_ERROR_STOP=1 -U postgres -d postgres -h localhost -p 54322 -f ./scripts/install-dbdev-with-test-helpers.sql + env: + PGPASSWORD: postgres + - name: Install core extension using dbdev cli + run: | + ~/.cargo/bin/dbdev install --connection postgres://postgres:postgres@localhost:54322/postgres --path . + psql -v ON_ERROR_STOP=1 -U postgres -d postgres -h localhost -p 54322 -c 'CREATE EXTENSION IF NOT EXISTS basejump_core with schema extensions;' + env: + PGPASSWORD: postgres + - name: Run Tests + run: supabase test db + stripe_tests: + name: Stripe Tests + runs-on: ubuntu-latest + needs: dbdev + steps: + - name: Download DBDev + uses: actions/download-artifact@master + with: + name: dbdev-output + path: ~/.cargo/bin + - name: Make dbdev executable + run: chmod +x ~/.cargo/bin/dbdev + - uses: actions/checkout@v3 + - uses: supabase/setup-cli@v1 + with: + version: latest + - name: Install stripe + run: | + # Download linux version as per Stripe documentation + curl -L https://github.com/stripe/stripe-cli/releases/download/v1.18.0/stripe_1.18.0_linux_x86_64.tar.gz --output stripe.tar.gz + # Unzip + tar -xvf stripe.tar.gz + - name: Create supabase/functions/.env including stripe webhook secrets + run: | + echo "STRIPE_WEBHOOK_SIGNING_SECRET=$(./stripe listen --print-secret)" >> supabase/functions/.env + echo "STRIPE_API_KEY=${{ secrets.STRIPE_API_KEY }}" >> supabase/functions/.env + echo "STRIPE_DEFAULT_PLAN_ID=${{ secrets.STRIPE_DEFAULT_PLAN_ID }}" >> supabase/functions/.env + echo "STRIPE_DEFAULT_TRIAL_DAYS=7" >> supabase/functions/.env + env: + STRIPE_API_KEY: ${{ secrets.STRIPE_API_KEY }} + STRIPE_DEFAULT_PLAN_ID: ${{ secrets.STRIPE_DEFAULT_PLAN_ID }} + - name: Setup and start Supabase + run: | + supabase init + echo -e "\n\n[functions.test-stripe-billing-webhooks]\nverify_jwt = false" >> supabase/config.toml + supabase start + - name: Install supabase_test_helpers from scripts/install-dbdev-with-test-helpers.sql (remove this once remote install enabled in dbdev CLI) + run: psql -v ON_ERROR_STOP=1 -U postgres -d postgres -h localhost -p 54322 -f ./scripts/install-dbdev-with-test-helpers.sql + env: + PGPASSWORD: postgres + - name: Install core extension using dbdev cli + run: | + ~/.cargo/bin/dbdev install --connection postgres://postgres:postgres@localhost:54322/postgres --path . + psql -v ON_ERROR_STOP=1 -U postgres -d postgres -h localhost -p 54322 -c 'CREATE EXTENSION IF NOT EXISTS basejump_core with schema extensions;' + env: + PGPASSWORD: postgres + - name: Yarn install supabase/tests/integration + run: cd supabase/tests/integration && yarn --frozen-lockfile + - name: Install playwright browsers + run: cd supabase/tests/integration && yarn test:setup + - name: Run tests without webhooks + run: cd supabase/tests/integration && yarn test:invalid && yarn test:stripe-no-webhooks + env: + SUPABASE_URL: http://127.0.0.1:54321 + SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY }} + STRIPE_API_KEY: ${{ secrets.STRIPE_API_KEY }} + STRIPE_DEFAULT_PLAN_ID: ${{ secrets.STRIPE_DEFAULT_PLAN_ID }} + STRIPE_DEFAULT_TRIAL_DAYS: 7 + - name: Run tests with webhooks + run: | + ./stripe listen --api-key ${{ secrets.STRIPE_API_KEY }} --forward-to http://127.0.0.1:54321/functions/v1/test-stripe-billing-webhooks & + cd supabase/tests/integration && yarn test:stripe-webhooks + env: + SUPABASE_URL: http://127.0.0.1:54321 + SUPABASE_ANON_KEY: ${{ secrets.SUPABASE_ANON_KEY }} + STRIPE_API_KEY: ${{ secrets.STRIPE_API_KEY }} + STRIPE_DEFAULT_PLAN_ID: ${{ secrets.STRIPE_DEFAULT_PLAN_ID }} + STRIPE_DEFAULT_TRIAL_DAYS: 7 \ No newline at end of file diff --git a/.gitignore b/.gitignore index ae117bc..183c0ae 100644 --- a/.gitignore +++ b/.gitignore @@ -2,9 +2,11 @@ # project dependencies .idea +.vscode # dependencies /node_modules +node_modules /.pnp .pnp.js @@ -39,6 +41,6 @@ yarn-error.log* *.tsbuildinfo next-env.d.ts -# Supabase -**/supabase/.branches -**/supabase/.temp +# turbo +.turbo + diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 6b68068..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "i18n-ally.localesPaths": [ - "content/locales" - ], - "deno.enable": true, - "deno.unstable": true, - "deno.enablePaths": [ - "supabase/functions" - ] -} \ No newline at end of file diff --git a/README.md b/README.md index 11636d9..bc55592 100644 --- a/README.md +++ b/README.md @@ -1,58 +1,70 @@ -# Basejump SaaS starter for Supabase +# Basejump -Basejump is an open source starter for Supabase. It provides personal accounts, shared team accounts, billing -subscriptions with Stripe and a dashboard template. +> If you're looking for the original Basejump which included a NextJS SaaS starter +> template, [check out the legacy repo](https://github.com/usebasejump/legacy-basejump-template). + +Basejump adds personal accounts, team accounts, permissions and billing support to Supabase Auth. [Learn more at usebasejump.com](https://usebasejump.com). -## Installation +## Features -```bash -yarn -yarn dev -``` +- **Personal accounts**: Every user that signs up using Supabase auth automatically gets their own personal account. + Billing on personal accounts can be enabled/disabled. +- **Team accounts**: Team accounts are billable accounts that can be shared by multiple users. Team accounts can be + disabled if you only wish to allow personal accounts. Billing on team accounts can also be disabled. +- **Permissions**: Permissions are handled using RLS, just like you're used to with Supabase. Basejump provides + convenience methods that let you restrict access to rows based on a user's account access and role within an account +- **Billing**: Basejump provides out of the box billing support for Stripe, but you can add your own providers easily. + If you do, please consider contributing them so others can benefit! +- **Testing**: Basejump is fully tested itself, but also provides a suite of testing tools that make it easier to test + your own Supabase functions and schema. You can check it out + at [database.dev/basejump/supabase_test_helpers](https://database.dev/basejump/supabase_test_helpers). You do not need + to be using Basejump to use the testing tools. -## Typescript and generated types +## Quick Start (recommended) -We've implemented automatic type generation based off of your Supabase database config. You can learn more about this -setup [in the supabase docs on type generation](https://supabase.com/docs/guides/api/generating-types) +Check out the getting started guide at [usebasejump.com](https://usebasejump.com). -To update your types, run: +## Contributing + +Yes please! Here's how you can get started locally + +#### Initialize Supabase ```bash -yarn generate-types + supabase init && supabase start ``` -You can then reference them as +#### Install dependencies using dbdev -```javascript -import Database from '@/types/supabase-types'; +1. Install dbdev according to instructions on [database.dev](https://database.dev). +2. Install supabase_test_helpers -const profile: Database['public']['Tables']['profiles']['Row'] = {name: 'John Doe'}; +```sql + select dbdev.install('basejump-supabase_test_helpers'); ``` -## Code Formatting and linting - -The project is configured to use ESLint and Prettier. Prettier is run through ESLint, not on its own. +#### Install local version of basejump_core -* Prettier: [Prettier ESLint Plugin](https://github.com/prettier/eslint-plugin-prettier) -* ESLint: [NextJS ESLint](https://nextjs.org/docs/basic-features/eslint) +```bash +dbdev install --connection postgres://postgres:postgres@localhost:54322/postgres --path . +``` -## Internationalizatoin and translations +#### Enable basejump_core -Basejump uses NextJS built in internationalization, and adds `next-translate` for translation support. +```sql + CREATE EXTENSION IF NOT EXISTS basejump_core with schema extensions; +``` -* [NextJS Internationalization](https://nextjs.org/docs/basic-features/i18n) -* [next-translate](https://github.com/aralroca/next-translate) +#### Make sure tests can run -## Thanks & Credits +```bash + supabase test db +``` -

Hosting has generously been provided by Vercel

- - Powered by Vercel - +### Add your changes and write tests. +Make sure you're following the database.dev upgrade guidelines. you should NEVER be updating/changing existing version +files. All changes should have valid migration files for postgres extensions. I'll try to flesh this section out more +later. diff --git a/__tests__/content/blog/en/article-1.md b/__tests__/content/blog/en/article-1.md deleted file mode 100644 index e7dfdc8..0000000 --- a/__tests__/content/blog/en/article-1.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: "Article 1" -published: 2022-10-15 ---- - -This is test article 1 \ No newline at end of file diff --git a/__tests__/content/blog/en/article-2.md b/__tests__/content/blog/en/article-2.md deleted file mode 100644 index 1367313..0000000 --- a/__tests__/content/blog/en/article-2.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -title: "Article 2" -published: 2022-10-16 -category: "Example Category" -description: "This is a description of the article" ---- - -This is test article 2 - diff --git a/__tests__/content/blog/en/unpublished-blog.md b/__tests__/content/blog/en/unpublished-blog.md deleted file mode 100644 index 62cb575..0000000 --- a/__tests__/content/blog/en/unpublished-blog.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: "An unpublished blog article" -category: "Updates" -published: ---- - -You should never see this! it's unpublished. If you wanted to publish it, just add a published date! \ No newline at end of file diff --git a/__tests__/content/docs/en/doc-2.md b/__tests__/content/docs/en/doc-2.md deleted file mode 100644 index ffd5a4b..0000000 --- a/__tests__/content/docs/en/doc-2.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: "Doc 2" -category: "Example Category" -published: 2022-10-16 ---- - -Test Doc 2 diff --git a/__tests__/content/docs/en/index.md b/__tests__/content/docs/en/index.md deleted file mode 100644 index 5c71b19..0000000 --- a/__tests__/content/docs/en/index.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: "Getting Started" -published: 2022-10-15 -description: "Basejump automatically generated documentation" ---- - -Basejump includes support for built-in documentation. You can check out some of the markdown formatting options on -the [formatting examples page](/docs/formatting-example). - -If you don't need documentation - not a problem! just remove the markdown files and navigation links for it. \ No newline at end of file diff --git a/__tests__/content/docs/en/unpublished-doc.md b/__tests__/content/docs/en/unpublished-doc.md deleted file mode 100644 index cba7be2..0000000 --- a/__tests__/content/docs/en/unpublished-doc.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: "Unpublished Doc" -published: ---- - -You should never see this! it's unpublished. If you wanted to publish it, just add a published date! \ No newline at end of file diff --git a/__tests__/core/input.test.tsx b/__tests__/core/input.test.tsx deleted file mode 100644 index 7854ce5..0000000 --- a/__tests__/core/input.test.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { act, render, screen } from "@tests/test-utils"; -import Input from "@/components/core/input"; - -describe("Input component", () => { - it("renders labels and help text", async () => { - await act(async () => { - await render(); - }); - - expect(screen.getByText("label-here")).toBeInTheDocument(); - expect(screen.getByText("help-text-here")).toBeInTheDocument(); - }); - - it("renders errors", async () => { - await act(async () => { - await render( - - ); - }); - - expect(screen.getByText("error-message-here")).toBeInTheDocument(); - expect(screen.getByText("error-message-here")).toHaveClass("text-error"); - }); -}); diff --git a/__tests__/core/select.test.tsx b/__tests__/core/select.test.tsx deleted file mode 100644 index 9006333..0000000 --- a/__tests__/core/select.test.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { act, render, screen } from "@tests/test-utils"; -import Select from "@/components/core/select"; - -describe("Input component", () => { - it("renders labels and help text", async () => { - await act(async () => { - await render( - ); - }); - - expect(screen.getByText("error-message-here")).toBeInTheDocument(); - expect(screen.getByText("error-message-here")).toHaveClass("text-error"); - }); -}); diff --git a/__tests__/dashboard/accounts/settings/update-team-member-role.test.tsx b/__tests__/dashboard/accounts/settings/update-team-member-role.test.tsx deleted file mode 100644 index 21ddd60..0000000 --- a/__tests__/dashboard/accounts/settings/update-team-member-role.test.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { act, render } from "@tests/test-utils"; -import { toast } from "react-toastify"; -import { ACCOUNT_ROLES } from "@/types/auth"; -import UpdateTeamMemberRole from "@/components/dashboard/accounts/settings/update-team-member-role"; -import { UseTeamMembersResponse } from "@/utils/api/use-team-members"; - -jest.spyOn(toast, "success"); - -describe("Update team member role", () => { - beforeEach(async () => { - await act(async () => { - const member = {} as UseTeamMembersResponse; - render(); - }); - }); - - test.skip("primary owners can change the primary owner", async () => { - jest.mock("@/utils/api/use-team-role", () => ({ - __esModule: true, - default: jest.fn(() => ({ - data: { - accountRole: ACCOUNT_ROLES.owner, - isPrimaryOwner: true, - }, - })), - })); - expect(false).toBeTruthy(); - // const nameInput = await screen.getByTestId("name"); - // const name = "Fred Flinstone"; - // await act(async () => { - // fireEvent.input(nameInput, { - // target: { - // value: name, - // }, - // }); - // - // fireEvent.submit(screen.getByRole("button")); - // }); - // - // expect(nameInput.value).toEqual(name); - // await waitFor( - // () => - // expect(fetch).toBeCalledWith( - // expect.stringContaining("profiles?id=eq.1234-5678"), - // expect.objectContaining({ - // method: "PATCH", - // body: JSON.stringify({ name }), - // }) - // ), - // { timeout: 1000 } - // ); - // expect(toast.success).toHaveBeenCalled(); - }); - - test.skip("Regular owners cannot change the primary owner", async () => { - // const nameInput = await screen.getByTestId("name"); - // await act(async () => { - // fireEvent.input(nameInput, { - // target: { - // value: "", - // }, - // }); - // fireEvent.submit(screen.getByRole("button")); - // }); - // - // await waitFor(() => - // expect(screen.getByTestId("name")).toHaveClass("input-error") - // ); - expect(false).toBeTruthy(); - }); - - test.skip("Primary owners only see the primary owner option on other owners", async () => { - expect(false).toBeTruthy(); - }); -}); diff --git a/__tests__/dashboard/profile/update-email.test.tsx b/__tests__/dashboard/profile/update-email.test.tsx deleted file mode 100644 index 51c12ef..0000000 --- a/__tests__/dashboard/profile/update-email.test.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { act, fireEvent, render, screen, waitFor } from "@tests/test-utils"; -import UpdateEmailAddress from "@/components/dashboard/profile/update-email-address"; -import { toast } from "react-toastify"; - -jest.spyOn(toast, "success"); - -const updateUser = jest.fn(({ email }) => { - return { - data: { - user: { - new_email: email, - }, - }, - }; -}); - -jest.mock("@supabase/auth-helpers-react", () => { - const original = jest.requireActual("@supabase/auth-helpers-react"); - return { - ...original, - useUser: jest.fn(() => ({ - id: "1234-5678", - email: "test@test.com", - })), - useSupabaseClient: () => ({ - auth: { - updateUser, - }, - }), - }; -}); - -describe("Update user email", () => { - beforeEach(async () => { - await act(async () => { - render(); - }); - }); - it("let's you update your email", async () => { - const emailInput = await screen.getByTestId("email"); - const email = "test2@test.com"; - await act(async () => { - fireEvent.input(emailInput, { - target: { - value: email, - }, - }); - - fireEvent.submit(screen.getByRole("button")); - }); - - expect(emailInput.value).toEqual(email); - expect(updateUser).toHaveBeenCalledWith({ - email, - }); - expect(toast.success).toHaveBeenCalled(); - }); - - it("Should error if the email is empty", async () => { - const emailInput = await screen.getByTestId("email"); - await act(async () => { - fireEvent.input(emailInput, { - target: { - value: "", - }, - }); - fireEvent.submit(screen.getByRole("button")); - }); - - await waitFor(() => - expect(screen.getByTestId("email")).toHaveClass("input-error") - ); - }); -}); diff --git a/__tests__/dashboard/profile/update-name.test.tsx b/__tests__/dashboard/profile/update-name.test.tsx deleted file mode 100644 index 03a2f65..0000000 --- a/__tests__/dashboard/profile/update-name.test.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import { act, fireEvent, render, screen, waitFor } from "@tests/test-utils"; -import UpdateProfileName from "@/components/dashboard/profile/update-profile-name"; -import { toast } from "react-toastify"; - -jest.spyOn(toast, "success"); - -jest.mock("@/utils/api/use-user-profile", () => ({ - __esModule: true, - default: jest.fn(() => ({ - data: { - name: "John Doe", - }, - })), -})); - -jest.mock("@supabase/auth-helpers-react", () => { - const original = jest.requireActual("@supabase/auth-helpers-react"); - return { - ...original, - useUser: jest.fn(() => ({ - id: "1234-5678", - })), - }; -}); - -describe("Update profile name", () => { - beforeEach(async () => { - await act(async () => { - render(); - }); - }); - it.skip("let's you update your profile name", async () => { - const nameInput = await screen.getByTestId("name"); - const name = "Fred Flinstone"; - await act(async () => { - fireEvent.input(nameInput, { - target: { - value: name, - }, - }); - - fireEvent.submit(screen.getByRole("button")); - }); - - expect(nameInput.value).toEqual(name); - await waitFor( - () => - expect(fetch).toBeCalledWith( - expect.stringContaining("profiles?id=eq.1234-5678"), - expect.objectContaining({ - method: "PATCH", - body: JSON.stringify({ name }), - }) - ), - { timeout: 1000 } - ); - expect(toast.success).toHaveBeenCalled(); - }); - - it("Should error if the name is empty", async () => { - const nameInput = await screen.getByTestId("name"); - await act(async () => { - fireEvent.input(nameInput, { - target: { - value: "", - }, - }); - fireEvent.submit(screen.getByRole("button")); - }); - - await waitFor(() => - expect(screen.getByTestId("name")).toHaveClass("input-error") - ); - }); -}); diff --git a/__tests__/setup/jest.setup.js b/__tests__/setup/jest.setup.js deleted file mode 100644 index c9c6b35..0000000 --- a/__tests__/setup/jest.setup.js +++ /dev/null @@ -1,20 +0,0 @@ -import "@testing-library/jest-dom"; -import { TextDecoder, TextEncoder } from "util"; -import fetchMock from "jest-fetch-mock"; - -/** - * This is a workaround for jsdom not supporting the TextEncoder and TextDecoder APIs. - */ -global.TextEncoder = TextEncoder; -global.TextDecoder = TextDecoder; - -/** - * Setup tests for mocking the fetch API. - */ -fetchMock.enableMocks(); - -beforeEach(() => { - // clear out the mocks - fetch.resetMocks(); - jest.clearAllMocks(); -}); diff --git a/__tests__/setup/test-utils.tsx b/__tests__/setup/test-utils.tsx deleted file mode 100644 index 9820f17..0000000 --- a/__tests__/setup/test-utils.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import React, { FC, ReactElement, useState } from "react"; -import { render, RenderOptions } from "@testing-library/react"; -import { Theme } from "react-daisyui"; -import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { createBrowserSupabaseClient } from "@supabase/auth-helpers-nextjs"; -import { ToastContainer } from "react-toastify"; -import { Database } from "@/types/supabase-types"; -import { SessionContextProvider } from "@supabase/auth-helpers-react/src/components/SessionContext"; - -const queryClient = new QueryClient(); - -const AllTheProviders: FC<{ children: ReactElement }> = ({ children }) => { - const [supabaseClient] = useState(() => - createBrowserSupabaseClient() - ); - - return ( - - - {children} - - - - ); -}; - -const customRender = ( - ui: ReactElement, - options?: Omit -) => render(ui, { wrapper: AllTheProviders, ...options }); - -export * from "@testing-library/react"; -export { customRender as render }; diff --git a/__tests__/utils/content-helpers.test.ts b/__tests__/utils/content-helpers.test.ts deleted file mode 100644 index 191f836..0000000 --- a/__tests__/utils/content-helpers.test.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { getContentPaths } from "@/utils/content/content-helpers"; - -describe("Content Helpers", () => { - it("Should know how to load and sort correct docs", async () => { - const docs = await getContentPaths("en", "docs"); - // should not load unpublished docs - expect(docs.length).toEqual(2); - // should sort them by published date - expect(docs[0].title).toEqual("Getting Started"); - // nextjs expects all dates to be strings - expect(typeof docs[0].meta.published).toEqual("string"); - }); - - it("Should know how to load and sort correct blogs", async () => { - const blogs = await getContentPaths("en", "blog"); - // should not load unpublished blogs - expect(blogs.length).toEqual(2); - // should sort them by published date - expect(blogs[0].title).toEqual("Article 1"); - // nextjs expects all dates to be strings - expect(typeof blogs[0].meta.published).toEqual("string"); - }); -}); diff --git a/__tests__/utils/handle-supabase-errors.test.ts b/__tests__/utils/handle-supabase-errors.test.ts deleted file mode 100644 index b35abed..0000000 --- a/__tests__/utils/handle-supabase-errors.test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import handleSupabaseErrors from "@/utils/handle-supabase-errors"; - -describe("Handle supabase responses", () => { - test("Should know how to throw an error if an error exists", () => { - expect(() => handleSupabaseErrors(null, { message: "error" })).toThrow( - "error" - ); - }); - - test("Should know how to not throw an error if no error exists", () => { - expect(() => handleSupabaseErrors({}, null)).not.toThrow(); - }); -}); diff --git a/__tests__/utils/slug-to-title.test.ts b/__tests__/utils/slug-to-title.test.ts deleted file mode 100644 index 3127fdc..0000000 --- a/__tests__/utils/slug-to-title.test.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { slugToTitle } from "@/utils/content/slug-to-title"; - -describe("Slug to title", () => { - test("Should know how to convert a slug into a title", () => { - expect(slugToTitle("hello-world")).toEqual("Hello World"); - expect(slugToTitle("hello-world_2")).toEqual("Hello World 2"); - expect(slugToTitle("hello-world-again-again")).toEqual( - "Hello World Again Again" - ); - }); - - test("Should know how to handle empty slugs", () => { - expect(slugToTitle("")).toEqual(""); - }); -}); diff --git a/basejump_core--2.0.0.sql b/basejump_core--2.0.0.sql new file mode 100644 index 0000000..ebf4556 --- /dev/null +++ b/basejump_core--2.0.0.sql @@ -0,0 +1,1355 @@ +/** + ____ _ + | _ \ (_) + | |_) | __ _ ___ ___ _ _ _ _ __ ___ _ __ + | _ < / _` / __|/ _ \ | | | | '_ ` _ \| '_ \ + | |_) | (_| \__ \ __/ | |_| | | | | | | |_) | + |____/ \__,_|___/\___| |\__,_|_| |_| |_| .__/ + _/ | | | + |__/ |_| + + Basejump is a starter kit for building SaaS products on top of Supabase. + */ + + +/** + * ------------------------------------------------------- + * Section - Basejump schema setup and utility functions + * ------------------------------------------------------- + */ + +-- revoke execution by default from public +ALTER DEFAULT PRIVILEGES REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC; +ALTER DEFAULT PRIVILEGES IN SCHEMA PUBLIC REVOKE EXECUTE ON FUNCTIONS FROM anon, authenticated; + +-- Create basejump schema +CREATE SCHEMA IF NOT EXISTS basejump; +GRANT USAGE ON SCHEMA basejump to authenticated; +GRANT USAGE ON SCHEMA basejump to service_role; + +/** + * ------------------------------------------------------- + * Section - Enums + * ------------------------------------------------------- + */ + +/** +* Subscription Status +* Tracks the current status of the account subscription +*/ +DO +$$ + BEGIN + IF NOT EXISTS(SELECT 1 + FROM pg_type t + JOIN pg_namespace n ON n.oid = t.typnamespace + WHERE t.typname = 'subscription_status' + AND n.nspname = 'basejump') THEN + create type basejump.subscription_status as enum ( + 'trialing', + 'active', + 'canceled', + 'incomplete', + 'incomplete_expired', + 'past_due', + 'unpaid' + ); + end if; + end; +$$; + +/** + * Account roles allow you to provide permission levels to users + * when they're acting on an account. By default, we provide + * "owner" and "member". The only distinction is that owners can + * also manage billing and invite/remove account members. + */ +DO +$$ + BEGIN + -- check it account_role already exists on basejump schema + IF NOT EXISTS(SELECT 1 + FROM pg_type t + JOIN pg_namespace n ON n.oid = t.typnamespace + WHERE t.typname = 'account_role' + AND n.nspname = 'basejump') THEN + CREATE TYPE basejump.account_role AS ENUM ('owner', 'member'); + end if; + end; +$$; + +/** + * Invitation types are either email or link. Email invitations are sent to + * a single user and can only be claimed once. Link invitations can be used multiple times + * Both expire after 24 hours + */ +DO +$$ + BEGIN + -- check it account_role already exists on basejump schema + IF NOT EXISTS(SELECT 1 + FROM pg_type t + JOIN pg_namespace n ON n.oid = t.typnamespace + WHERE t.typname = 'invitation_type' + AND n.nspname = 'basejump') THEN + CREATE TYPE basejump.invitation_type AS ENUM ('one_time', '24_hour'); + end if; + end; +$$; + +/** + * ------------------------------------------------------- + * Section - Basejump settings + * ------------------------------------------------------- + */ + +CREATE TABLE IF NOT EXISTS basejump.config +( + enable_team_accounts boolean default true, + enable_personal_account_billing boolean default true, + enable_team_account_billing boolean default true, + billing_provider text default 'stripe' +); + +-- create config row +INSERT INTO basejump.config (enable_team_accounts, enable_personal_account_billing, enable_team_account_billing) +VALUES (true, true, true); + +-- enable select on the config table +GRANT SELECT ON basejump.config TO authenticated, service_role; + +-- enable RLS on config +ALTER TABLE basejump.config + ENABLE ROW LEVEL SECURITY; + +create policy "Basejump settings can be read by authenticated users" on basejump.config + for select + to authenticated + using ( + true + ); + +/** + * ------------------------------------------------------- + * Section - Basejump utility functions + * ------------------------------------------------------- + */ + +/** + basejump.get_config() + Get the full config object to check basejump settings + This is not accessible from the outside, so can only be used inside postgres functions + */ +CREATE OR REPLACE FUNCTION basejump.get_config() + RETURNS json AS +$$ +DECLARE + result RECORD; +BEGIN + SELECT * from basejump.config limit 1 into result; + return row_to_json(result); +END; +$$ LANGUAGE plpgsql; + +grant execute on function basejump.get_config() to authenticated, service_role; + + +/** + basejump.is_set("field_name") + Check a specific boolean config value + */ +CREATE OR REPLACE FUNCTION basejump.is_set(field_name text) + RETURNS boolean AS +$$ +DECLARE + result BOOLEAN; +BEGIN + execute format('select %I from basejump.config limit 1', field_name) into result; + return result; +END; +$$ LANGUAGE plpgsql; + +grant execute on function basejump.is_set(text) to authenticated; + + +/** + * Automatic handling for maintaining created_at and updated_at timestamps + * on tables + */ +CREATE OR REPLACE FUNCTION basejump.trigger_set_timestamps() + RETURNS TRIGGER AS +$$ +BEGIN + if TG_OP = 'INSERT' then + NEW.created_at = now(); + NEW.updated_at = now(); + else + NEW.updated_at = now(); + NEW.created_at = OLD.created_at; + end if; + RETURN NEW; +END +$$ LANGUAGE plpgsql; + + +/** + * Automatic handling for maintaining created_by and updated_by timestamps + * on tables + */ +CREATE OR REPLACE FUNCTION basejump.trigger_set_user_tracking() + RETURNS TRIGGER AS +$$ +BEGIN + if TG_OP = 'INSERT' then + NEW.created_by = auth.uid(); + NEW.updated_by = auth.uid(); + else + NEW.updated_by = auth.uid(); + NEW.created_by = OLD.created_by; + end if; + RETURN NEW; +END +$$ LANGUAGE plpgsql; + +/** + basejump.generate_token(length) + Generates a secure token - used internally for invitation tokens + but could be used elsewhere. Check out the invitations table for more info on + how it's used + */ +CREATE OR REPLACE FUNCTION basejump.generate_token(length int) + RETURNS text AS +$$ +select regexp_replace(replace( + replace(replace(replace(encode(gen_random_bytes(length)::bytea, 'base64'), '/', ''), '+', + ''), '\', ''), + '=', + ''), E'[\\n\\r]+', '', 'g'); +$$ LANGUAGE sql; + +grant execute on function basejump.generate_token(int) to authenticated; + + +/** + * ------------------------------------------------------- + * Section - Accounts + * ------------------------------------------------------- + */ + +/** + * Accounts are the primary grouping for most objects within + * the system. They have many users, and all billing is connected to + * an account. + */ +CREATE TABLE IF NOT EXISTS basejump.accounts +( + id uuid unique NOT NULL DEFAULT uuid_generate_v4(), + -- defaults to the user who creates the account + -- this user cannot be removed from an account without changing + -- the primary owner first + primary_owner_user_id uuid references auth.users not null default auth.uid(), + -- Account name + name text, + slug text unique, + personal_account boolean default false not null, + updated_at timestamp with time zone, + created_at timestamp with time zone, + created_by uuid references auth.users, + updated_by uuid references auth.users, + private_metadata jsonb default '{}'::jsonb, + public_metadata jsonb default '{}'::jsonb, + PRIMARY KEY (id) +); + +-- constraint that conditionally allows nulls on the slug ONLY if personal_account is true +-- remove this if you want to ignore accounts slugs entirely +ALTER TABLE basejump.accounts + ADD CONSTRAINT basejump_accounts_slug_null_if_personal_account_true CHECK ( + (personal_account = true AND slug is null) + OR (personal_account = false AND slug is not null) + ); + +-- Open up access to accounts +GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE basejump.accounts TO authenticated, service_role; + +/** + * We want to protect some fields on accounts from being updated + * Specifically the primary owner user id and account id. + * primary_owner_user_id should be updated using the dedicated function + */ +CREATE OR REPLACE FUNCTION basejump.protect_account_fields() + RETURNS TRIGGER AS +$$ +BEGIN + IF current_user IN ('authenticated', 'anon') THEN + -- these are protected fields that users are not allowed to update themselves + -- platform admins should be VERY careful about updating them as well. + if NEW.id <> OLD.id + OR NEW.personal_account <> OLD.personal_account + OR NEW.primary_owner_user_id <> OLD.primary_owner_user_id + THEN + RAISE EXCEPTION 'You do not have permission to update this field'; + end if; + end if; + + RETURN NEW; +END +$$ LANGUAGE plpgsql; + +-- trigger to protect account fields +CREATE TRIGGER basejump_protect_account_fields + BEFORE UPDATE + ON basejump.accounts + FOR EACH ROW +EXECUTE FUNCTION basejump.protect_account_fields(); + +-- convert any character in the slug that's not a letter, number, or dash to a dash on insert/update for accounts +CREATE OR REPLACE FUNCTION basejump.slugify_account_slug() + RETURNS TRIGGER AS +$$ +BEGIN + if NEW.slug is not null then + NEW.slug = lower(regexp_replace(NEW.slug, '[^a-zA-Z0-9-]+', '-', 'g')); + end if; + + RETURN NEW; +END +$$ LANGUAGE plpgsql; + +-- trigger to slugify the account slug +CREATE TRIGGER basejump_slugify_account_slug + BEFORE INSERT OR UPDATE + ON basejump.accounts + FOR EACH ROW +EXECUTE FUNCTION basejump.slugify_account_slug(); + +-- enable RLS for accounts +alter table basejump.accounts + enable row level security; + +-- protect the timestamps +CREATE TRIGGER basejump_set_accounts_timestamp + BEFORE INSERT OR UPDATE + ON basejump.accounts + FOR EACH ROW +EXECUTE PROCEDURE basejump.trigger_set_timestamps(); + +-- set the user tracking +CREATE TRIGGER basejump_set_accounts_user_tracking + BEFORE INSERT OR UPDATE + ON basejump.accounts + FOR EACH ROW +EXECUTE PROCEDURE basejump.trigger_set_user_tracking(); + +/** + * Account users are the users that are associated with an account. + * They can be invited to join the account, and can have different roles. + * The system does not enforce any permissions for roles, other than restricting + * billing and account membership to only owners + */ +create table if not exists basejump.account_user +( + -- id of the user in the account + user_id uuid references auth.users on delete cascade not null, + -- id of the account the user is in + account_id uuid references basejump.accounts on delete cascade not null, + -- role of the user in the account + account_role basejump.account_role not null, + constraint account_user_pkey primary key (user_id, account_id) +); + +GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE basejump.account_user TO authenticated, service_role; + + +-- enable RLS for account_user +alter table basejump.account_user + enable row level security; + +/** + * When an account gets created, we want to insert the current user as the first + * owner + */ +create or replace function basejump.add_current_user_to_new_account() + returns trigger + language plpgsql + security definer + set search_path = public +as +$$ +begin + if new.primary_owner_user_id = auth.uid() then + insert into basejump.account_user (account_id, user_id, account_role) + values (NEW.id, auth.uid(), 'owner'); + end if; + return NEW; +end; +$$; + +-- trigger the function whenever a new account is created +CREATE TRIGGER basejump_add_current_user_to_new_account + AFTER INSERT + ON basejump.accounts + FOR EACH ROW +EXECUTE FUNCTION basejump.add_current_user_to_new_account(); + +/** + * When a user signs up, we need to create a personal account for them + * and add them to the account_user table so they can act on it + */ +create or replace function basejump.run_new_user_setup() + returns trigger + language plpgsql + security definer + set search_path = public +as +$$ +declare + first_account_id uuid; + generated_user_name text; +begin + + -- first we setup the user profile + -- TODO: see if we can get the user's name from the auth.users table once we learn how oauth works + if new.email IS NOT NULL then + generated_user_name := split_part(new.email, '@', 1); + end if; + -- create the new users's personal account + insert into basejump.accounts (name, primary_owner_user_id, personal_account, id) + values (generated_user_name, NEW.id, true, NEW.id) + returning id into first_account_id; + + -- add them to the account_user table so they can act on it + insert into basejump.account_user (account_id, user_id, account_role) + values (first_account_id, NEW.id, 'owner'); + + return NEW; +end; +$$; + +-- trigger the function every time a user is created +create trigger on_auth_user_created + after insert + on auth.users + for each row +execute procedure basejump.run_new_user_setup(); + +/** + * ------------------------------------------------------- + * Section - Billing + * ------------------------------------------------------- + */ + +/** + * Billing customer + * This is a private table that contains a mapping of user IDs to your billing providers IDs + */ +create table if not exists basejump.billing_customers +( + -- UUID from auth.users + account_id uuid references basejump.accounts (id) on delete cascade not null, + -- The user's customer ID in Stripe. User must not be able to update this. + id text primary key, + -- The email address the customer wants to use for invoicing + email text, + -- The active status of a customer + active boolean, + -- The billing provider the customer is using + provider text +); + +-- Open up access to billing_customers +GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE basejump.billing_customers TO service_role; +GRANT SELECT ON TABLE basejump.billing_customers TO authenticated; + + +-- enable RLS for billing_customers +alter table + basejump.billing_customers + enable row level security; + +/** + * Billing subscriptions + * This is a private table that contains a mapping of account IDs to your billing providers subscription IDs + */ +create table if not exists basejump.billing_subscriptions +( + -- Subscription ID from Stripe, e.g. sub_1234. + id text primary key, + account_id uuid references basejump.accounts (id) on delete cascade not null, + billing_customer_id text references basejump.billing_customers (id) on delete cascade not null, + -- The status of the subscription object, one of subscription_status type above. + status basejump.subscription_status, + -- Set of key-value pairs, used to store additional information about the object in a structured format. + metadata jsonb, + -- ID of the price that created this subscription. + price_id text, + plan_name text, + -- Quantity multiplied by the unit amount of the price creates the amount of the subscription. Can be used to charge multiple seats. + quantity integer, + -- If true the subscription has been canceled by the user and will be deleted at the end of the billing period. + cancel_at_period_end boolean, + -- Time at which the subscription was created. + created timestamp with time zone default timezone('utc' :: text, now()) not null, + -- Start of the current period that the subscription has been invoiced for. + current_period_start timestamp with time zone default timezone('utc' :: text, now()) not null, + -- End of the current period that the subscription has been invoiced for. At the end of this period, a new invoice will be created. + current_period_end timestamp with time zone default timezone('utc' :: text, now()) not null, + -- If the subscription has ended, the timestamp of the date the subscription ended. + ended_at timestamp with time zone default timezone('utc' :: text, now()), + -- A date in the future at which the subscription will automatically get canceled. + cancel_at timestamp with time zone default timezone('utc' :: text, now()), + -- If the subscription has been canceled, the date of that cancellation. If the subscription was canceled with `cancel_at_period_end`, `canceled_at` will still reflect the date of the initial cancellation request, not the end of the subscription period when the subscription is automatically moved to a canceled state. + canceled_at timestamp with time zone default timezone('utc' :: text, now()), + -- If the subscription has a trial, the beginning of that trial. + trial_start timestamp with time zone default timezone('utc' :: text, now()), + -- If the subscription has a trial, the end of that trial. + trial_end timestamp with time zone default timezone('utc' :: text, now()), + provider text +); + +-- Open up access to billing_subscriptions +GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE basejump.billing_subscriptions TO service_role; +GRANT SELECT ON TABLE basejump.billing_subscriptions TO authenticated; + +-- enable RLS for billing_subscriptions +alter table + basejump.billing_subscriptions + enable row level security; + +/** + * ------------------------------------------------------- + * Section - Invitations + * ------------------------------------------------------- + */ + +/** + * Invitations are sent to users to join a account + * They pre-define the role the user should have once they join + */ +create table if not exists basejump.invitations +( + -- the id of the invitation + id uuid unique not null default uuid_generate_v4(), + -- what role should invitation accepters be given in this account + account_role basejump.account_role not null, + -- the account the invitation is for + account_id uuid references basejump.accounts (id) on delete cascade not null, + -- unique token used to accept the invitation + token text unique not null default basejump.generate_token(30), + -- who created the invitation + invited_by_user_id uuid references auth.users not null, + -- account name. filled in by a trigger + account_name text, + -- when the invitation was last updated + updated_at timestamp with time zone, + -- when the invitation was created + created_at timestamp with time zone, + -- what type of invitation is this + invitation_type basejump.invitation_type not null, + primary key (id) +); + +-- Open up access to invitations +GRANT SELECT, INSERT, UPDATE, DELETE ON TABLE basejump.invitations TO authenticated, service_role; + +-- manage timestamps +CREATE TRIGGER basejump_set_invitations_timestamp + BEFORE INSERT OR UPDATE + ON basejump.invitations + FOR EACH ROW +EXECUTE FUNCTION basejump.trigger_set_timestamps(); + +/** + * This funciton fills in account info and inviting user email + * so that the recipient can get more info about the invitation prior to + * accepting. It allows us to avoid complex permissions on accounts + */ +CREATE OR REPLACE FUNCTION basejump.trigger_set_invitation_details() + RETURNS TRIGGER AS +$$ +BEGIN + NEW.invited_by_user_id = auth.uid(); + NEW.account_name = (select name from basejump.accounts where id = NEW.account_id); + RETURN NEW; +END +$$ LANGUAGE plpgsql; + +CREATE TRIGGER basejump_trigger_set_invitation_details + BEFORE INSERT + ON basejump.invitations + FOR EACH ROW +EXECUTE FUNCTION basejump.trigger_set_invitation_details(); + +-- enable RLS on invitations +alter table basejump.invitations + enable row level security; + +/** + * ------------------------------------------------------- + * Section - Internal functions + * ------------------------------------------------------- + * These functions are stored on the basejump schema, and useful for things like + * generating RLS policies + */ + +/** + * Returns true if the current user has the pass in role on the passed in account + * If no role is sent, will return true if the user is a member of the account + */ +create or replace function basejump.has_role_on_account(account_id uuid, account_role basejump.account_role default null) + returns boolean + language sql + security definer + set search_path = public +as +$$ +select exists( + select 1 + from basejump.account_user wu + where wu.user_id = auth.uid() + and wu.account_id = has_role_on_account.account_id + and ( + wu.account_role = has_role_on_account.account_role + or has_role_on_account.account_role is null + ) + ); +$$; + +grant execute on function basejump.has_role_on_account(uuid, basejump.account_role) to authenticated; + + +/** + * Returns account_ids that the current user is a member of. If you pass in a role, + * it'll only return accounts that the user is a member of with that role. + */ +create or replace function basejump.get_accounts_with_role(passed_in_role basejump.account_role default null) + returns setof uuid + language sql + security definer + set search_path = public +as +$$ +select account_id +from basejump.account_user wu +where wu.user_id = auth.uid() + and ( + wu.account_role = passed_in_role + or passed_in_role is null + ); +$$; + +grant execute on function basejump.get_accounts_with_role(basejump.account_role) to authenticated; + +/** + * ------------------------------------------------------- + * Section - Public functions + * ------------------------------------------------------- + * Each of these functions exists in the public name space because they are accessible + * via the API. it is the primary way developers can interact with Basejump accounts + */ + +/** +* Returns the account_id for a given account slug +*/ + +create or replace function public.get_account_id(slug text) + returns uuid + language sql +as +$$ +select id +from basejump.accounts +where slug = get_account_id.slug; +$$; + +grant execute on function public.get_account_id(text) to authenticated, service_role; + +/** + * Returns the current user's role within a given account_id +*/ +create or replace function public.current_user_account_role(account_id uuid) + returns jsonb + language plpgsql +as +$$ +DECLARE + response jsonb; +BEGIN + + select jsonb_build_object( + 'account_role', wu.account_role, + 'is_primary_owner', a.primary_owner_user_id = auth.uid(), + 'is_personal_account', a.personal_account + ) + into response + from basejump.account_user wu + join basejump.accounts a on a.id = wu.account_id + where wu.user_id = auth.uid() + and wu.account_id = current_user_account_role.account_id; + + -- if the user is not a member of the account, throw an error + if response ->> 'account_role' IS NULL then + raise exception 'Not found'; + end if; + + return response; +END +$$; + +grant execute on function public.current_user_account_role(uuid) to authenticated; + +/** + * Returns the current billing status for an account + */ +CREATE OR REPLACE FUNCTION public.get_account_billing_status(account_id uuid) + RETURNS jsonb + security definer + set search_path = public, basejump +AS +$$ +DECLARE + result jsonb; + role_result jsonb; +BEGIN + select public.current_user_account_role(get_account_billing_status.account_id) into role_result; + + select jsonb_build_object( + 'account_id', get_account_billing_status.account_id, + 'billing_subscription_id', s.id, + 'billing_enabled', case + when a.personal_account = true then config.enable_personal_account_billing + else config.enable_team_account_billing end, + 'billing_status', s.status, + 'billing_customer_id', c.id, + 'billing_provider', config.billing_provider, + 'billing_email', + coalesce(c.email, u.email) -- if we don't have a customer email, use the user's email as a fallback + ) + into result + from basejump.accounts a + join auth.users u on u.id = a.primary_owner_user_id + left join basejump.billing_subscriptions s on s.account_id = a.id + left join basejump.billing_customers c on c.account_id = coalesce(s.account_id, a.id) + join basejump.config config on true + where a.id = get_account_billing_status.account_id + order by s.created desc + limit 1; + + return result || role_result; +END; +$$ LANGUAGE plpgsql; + +grant execute on function public.get_account_billing_status(uuid) to authenticated; + +/** + * Allow service accounts to upsert the billing data for an account + */ +CREATE OR REPLACE FUNCTION public.service_role_upsert_customer_subscription(account_id uuid, + customer jsonb default null, + subscription jsonb default null) + RETURNS void AS +$$ +BEGIN + -- if the customer is not null, upsert the data into billing_customers, only upsert fields that are present in the jsonb object + if customer is not null then + insert into basejump.billing_customers (id, account_id, email, provider) + values (customer ->> 'id', service_role_upsert_customer_subscription.account_id, customer ->> 'billing_email', + (customer ->> 'provider')) + on conflict (id) do update + set email = customer ->> 'billing_email'; + end if; + + -- if the subscription is not null, upsert the data into billing_subscriptions, only upsert fields that are present in the jsonb object + if subscription is not null then + insert into basejump.billing_subscriptions (id, account_id, billing_customer_id, status, metadata, price_id, + quantity, cancel_at_period_end, created, current_period_start, + current_period_end, ended_at, cancel_at, canceled_at, trial_start, + trial_end, plan_name, provider) + values (subscription ->> 'id', service_role_upsert_customer_subscription.account_id, + subscription ->> 'billing_customer_id', (subscription ->> 'status')::basejump.subscription_status, + subscription -> 'metadata', + subscription ->> 'price_id', (subscription ->> 'quantity')::int, + (subscription ->> 'cancel_at_period_end')::boolean, + (subscription ->> 'created')::timestamptz, (subscription ->> 'current_period_start')::timestamptz, + (subscription ->> 'current_period_end')::timestamptz, (subscription ->> 'ended_at')::timestamptz, + (subscription ->> 'cancel_at')::timestamptz, + (subscription ->> 'canceled_at')::timestamptz, (subscription ->> 'trial_start')::timestamptz, + (subscription ->> 'trial_end')::timestamptz, + subscription ->> 'plan_name', (subscription ->> 'provider')) + on conflict (id) do update + set billing_customer_id = subscription ->> 'billing_customer_id', + status = (subscription ->> 'status')::basejump.subscription_status, + metadata = subscription -> 'metadata', + price_id = subscription ->> 'price_id', + quantity = (subscription ->> 'quantity')::int, + cancel_at_period_end = (subscription ->> 'cancel_at_period_end')::boolean, + current_period_start = (subscription ->> 'current_period_start')::timestamptz, + current_period_end = (subscription ->> 'current_period_end')::timestamptz, + ended_at = (subscription ->> 'ended_at')::timestamptz, + cancel_at = (subscription ->> 'cancel_at')::timestamptz, + canceled_at = (subscription ->> 'canceled_at')::timestamptz, + trial_start = (subscription ->> 'trial_start')::timestamptz, + trial_end = (subscription ->> 'trial_end')::timestamptz, + plan_name = subscription ->> 'plan_name'; + end if; +end; +$$ LANGUAGE plpgsql; + +GRANT EXECUTE ON FUNCTION public.service_role_upsert_customer_subscription(uuid, jsonb, jsonb) TO service_role; + + +/** + * Let's you update a users role within an account if you are an owner of that account + **/ +create or replace function public.update_account_user_role(account_id uuid, user_id uuid, + new_account_role basejump.account_role, + make_primary_owner boolean default false) + returns void + security definer + set search_path = public + language plpgsql +as +$$ +declare + is_account_owner boolean; + is_account_primary_owner boolean; + changing_primary_owner boolean; +begin + -- check if the user is an owner, and if they are, allow them to update the role + select basejump.has_role_on_account(update_account_user_role.account_id, 'owner') into is_account_owner; + + if not is_account_owner then + raise exception 'You must be an owner of the account to update a users role'; + end if; + + -- check if the user being changed is the primary owner, if so its not allowed + select primary_owner_user_id = auth.uid(), primary_owner_user_id = update_account_user_role.user_id + into is_account_primary_owner, changing_primary_owner + from basejump.accounts + where id = update_account_user_role.account_id; + + if changing_primary_owner = true and is_account_primary_owner = false then + raise exception 'You must be the primary owner of the account to change the primary owner'; + end if; + + update basejump.account_user au + set account_role = new_account_role + where au.account_id = update_account_user_role.account_id + and au.user_id = update_account_user_role.user_id; + + if make_primary_owner = true then + -- first we see if the current user is the owner, only they can do this + if is_account_primary_owner = false then + raise exception 'You must be the primary owner of the account to change the primary owner'; + end if; + + update basejump.accounts + set primary_owner_user_id = update_account_user_role.user_id + where id = update_account_user_role.account_id; + end if; +end; +$$; + +grant execute on function public.update_account_user_role(uuid, uuid, basejump.account_role, boolean) to authenticated; + +/** + Returns the current user's accounts + */ +create or replace function public.get_accounts() + returns json + language sql +as +$$ +select coalesce(json_agg( + json_build_object( + 'account_id', wu.account_id, + 'account_role', wu.account_role, + 'is_primary_owner', a.primary_owner_user_id = auth.uid(), + 'name', a.name, + 'slug', a.slug, + 'personal_account', a.personal_account, + 'created_at', a.created_at, + 'updated_at', a.updated_at + ) + ), '[]'::json) +from basejump.account_user wu + join basejump.accounts a on a.id = wu.account_id +where wu.user_id = auth.uid(); +$$; + +grant execute on function public.get_accounts() to authenticated; + +/** + Returns a specific account that the current user has access to + */ +create or replace function public.get_account(account_id uuid) + returns json + language plpgsql +as +$$ +BEGIN + -- check if the user is a member of the account or a service_role user + if current_user IN ('anon', 'authenticated') and + (select current_user_account_role(get_account.account_id) ->> 'account_role' IS NULL) then + raise exception 'You must be a member of an account to access it'; + end if; + + + return (select json_build_object( + 'account_id', a.id, + 'account_role', wu.account_role, + 'is_primary_owner', a.primary_owner_user_id = auth.uid(), + 'name', a.name, + 'slug', a.slug, + 'personal_account', a.personal_account, + 'billing_enabled', case + when a.personal_account = true then + config.enable_personal_account_billing + else + config.enable_team_account_billing + end, + 'billing_status', bs.status, + 'created_at', a.created_at, + 'updated_at', a.updated_at, + 'metadata', a.public_metadata + ) + from basejump.accounts a + left join basejump.account_user wu on a.id = wu.account_id and wu.user_id = auth.uid() + join basejump.config config on true + left join (select bs.account_id, status + from basejump.billing_subscriptions bs + where bs.account_id = get_account.account_id + order by created desc + limit 1) bs on bs.account_id = a.id + where a.id = get_account.account_id); +END; +$$; + +grant execute on function public.get_account(uuid) to authenticated, service_role; + +/** + Returns a specific account that the current user has access to + */ +create or replace function public.get_account_by_slug(slug text) + returns json + language plpgsql +as +$$ +DECLARE + internal_account_id uuid; +BEGIN + select a.id + into internal_account_id + from basejump.accounts a + where a.slug IS NOT NULL + and a.slug = get_account_by_slug.slug; + + return public.get_account(internal_account_id); +END; +$$; + +grant execute on function public.get_account_by_slug(text) to authenticated; + +/** + Returns the personal account for the current user + */ +create or replace function public.get_personal_account() + returns json + language plpgsql +as +$$ +BEGIN + return public.get_account(auth.uid()); +END; +$$; + +grant execute on function public.get_personal_account() to authenticated; + +/** + * Create an account + */ +create or replace function public.create_account(slug text default null, name text default null) + returns json + language plpgsql +as +$$ +DECLARE + new_account_id uuid; +BEGIN + insert into basejump.accounts (slug, name) + values (create_account.slug, create_account.name) + returning id into new_account_id; + + return public.get_account(new_account_id); +EXCEPTION + WHEN unique_violation THEN + raise exception 'An account with that unique ID already exists'; +END; +$$; + +grant execute on function public.create_account(slug text, name text) to authenticated; + +/** + Update an account with passed in info. None of the info is required except for account ID. + If you don't pass in a value for a field, it will not be updated. + If you set replace_meta to true, the metadata will be replaced with the passed in metadata. + If you set replace_meta to false, the metadata will be merged with the passed in metadata. + */ +create or replace function public.update_account(account_id uuid, slug text default null, name text default null, + public_metadata jsonb default null, + replace_metadata boolean default false) + returns json + language plpgsql +as +$$ +BEGIN + + -- check if postgres role is service_role + if current_user IN ('anon', 'authenticated') and + not (select current_user_account_role(update_account.account_id) ->> 'account_role' = 'owner') then + raise exception 'Only account owners can update an account'; + end if; + + update basejump.accounts accounts + set slug = coalesce(update_account.slug, accounts.slug), + name = coalesce(update_account.name, accounts.name), + public_metadata = case + when update_account.public_metadata is null then accounts.public_metadata -- do nothing + when accounts.public_metadata IS NULL then update_account.public_metadata -- set metadata + when update_account.replace_metadata + then update_account.public_metadata -- replace metadata + else accounts.public_metadata || update_account.public_metadata end -- merge metadata + where accounts.id = update_account.account_id; + + return public.get_account(account_id); +END; +$$; + +grant execute on function public.update_account(uuid, text, text, jsonb, boolean) to authenticated, service_role; + +/** + Returns a list of current account members. Only account owners can access this function. + It's a security definer because it requries us to lookup personal_accounts for existing members so we can + get their names. + */ +create or replace function public.get_account_members(account_id uuid, results_limit integer default 50, + results_offset integer default 0) + returns json + language plpgsql + security definer + set search_path = basejump +as +$$ +BEGIN + + -- only account owners can access this function + if (select public.current_user_account_role(get_account_members.account_id) ->> 'account_role' <> 'owner') then + raise exception 'Only account owners can access this function'; + end if; + + return (select json_agg( + json_build_object( + 'user_id', wu.user_id, + 'account_role', wu.account_role, + 'name', p.name, + 'email', u.email, + 'is_primary_owner', a.primary_owner_user_id = wu.user_id + ) + ) + from basejump.account_user wu + join basejump.accounts a on a.id = wu.account_id + join basejump.accounts p on p.primary_owner_user_id = wu.user_id and p.personal_account = true + join auth.users u on u.id = wu.user_id + where wu.account_id = get_account_members.account_id + limit coalesce(get_account_members.results_limit, 50) offset coalesce(get_account_members.results_offset, 0)); +END; +$$; + +grant execute on function public.get_account_members(uuid, integer, integer) to authenticated; + +/** + Allows an owner of the account to remove any member other than the primary owner + */ + +create or replace function public.remove_account_member(account_id uuid, user_id uuid) + returns void + language plpgsql +as +$$ +BEGIN + -- only account owners can access this function + if basejump.has_role_on_account(remove_account_member.account_id, 'owner') <> true then + raise exception 'Only account owners can access this function'; + end if; + + delete + from basejump.account_user wu + where wu.account_id = remove_account_member.account_id + and wu.user_id = remove_account_member.user_id; +END; +$$; + +grant execute on function public.remove_account_member(uuid, uuid) to authenticated; + +/** + Returns a list of currently active invitations for a given account + */ + +create or replace function public.get_account_invitations(account_id uuid, results_limit integer default 25, + results_offset integer default 0) + returns json + language plpgsql +as +$$ +BEGIN + -- only account owners can access this function + if (select public.current_user_account_role(get_account_invitations.account_id) ->> 'account_role' <> 'owner') then + raise exception 'Only account owners can access this function'; + end if; + + return (select json_agg( + json_build_object( + 'account_role', i.account_role, + 'created_at', i.created_at, + 'invitation_type', i.invitation_type, + 'invitation_id', i.id + ) + ) + from basejump.invitations i + where i.account_id = get_account_invitations.account_id + and i.created_at > now() - interval '24 hours' + limit coalesce(get_account_invitations.results_limit, 25) offset coalesce(get_account_invitations.results_offset, 0)); +END; +$$; + +grant execute on function public.get_account_invitations(uuid, integer, integer) to authenticated; + + +/** + * Allows a user to accept an existing invitation and join a account + * This one exists in the public schema because we want it to be called + * using the supabase rpc method + */ +create or replace function public.accept_invitation(lookup_invitation_token text) + returns jsonb + language plpgsql + security definer set search_path = public, basejump +as +$$ +declare + lookup_account_id uuid; + declare new_member_role basejump.account_role; + lookup_account_slug text; +begin + select i.account_id, i.account_role, a.slug + into lookup_account_id, new_member_role, lookup_account_slug + from basejump.invitations i + join basejump.accounts a on a.id = i.account_id + where i.token = lookup_invitation_token + and i.created_at > now() - interval '24 hours'; + + if lookup_account_id IS NULL then + raise exception 'Invitation not found'; + end if; + + if lookup_account_id is not null then + -- we've validated the token is real, so grant the user access + insert into basejump.account_user (account_id, user_id, account_role) + values (lookup_account_id, auth.uid(), new_member_role); + -- email types of invitations are only good for one usage + delete from basejump.invitations where token = lookup_invitation_token and invitation_type = 'one_time'; + end if; + return json_build_object('account_id', lookup_account_id, 'account_role', new_member_role, 'slug', + lookup_account_slug); +EXCEPTION + WHEN unique_violation THEN + raise exception 'You are already a member of this account'; +end; +$$; + +grant execute on function public.accept_invitation(text) to authenticated; + + +/** + * Allows a user to lookup an existing invitation and join a account + * This one exists in the public schema because we want it to be called + * using the supabase rpc method + */ +create or replace function public.lookup_invitation(lookup_invitation_token text) + returns json + language plpgsql + security definer set search_path = public, basejump +as +$$ +declare + name text; + invitation_active boolean; +begin + select account_name, + case when id IS NOT NULL then true else false end as active + into name, invitation_active + from basejump.invitations + where token = lookup_invitation_token + and created_at > now() - interval '24 hours' + limit 1; + return json_build_object('active', coalesce(invitation_active, false), 'account_name', name); +end; +$$; + +grant execute on function public.lookup_invitation(text) to authenticated; + + +/** + Allows a user to create a new invitation if they are an owner of an account + */ +create or replace function public.create_invitation(account_id uuid, account_role basejump.account_role, + invitation_type basejump.invitation_type) + returns json + language plpgsql +as +$$ +declare + new_invitation basejump.invitations; +begin + insert into basejump.invitations (account_id, account_role, invitation_type, invited_by_user_id) + values (account_id, account_role, invitation_type, auth.uid()) + returning * into new_invitation; + + return json_build_object('token', new_invitation.token); +end +$$; + +grant execute on function public.create_invitation(uuid, basejump.account_role, basejump.invitation_type) to authenticated; + +/** + Allows an owner to delete an existing invitation + */ + +create or replace function public.delete_invitation(invitation_id uuid) + returns void + language plpgsql +as +$$ +begin + -- verify account owner for the invitation + if basejump.has_role_on_account( + (select account_id from basejump.invitations where id = delete_invitation.invitation_id), 'owner') <> + true then + raise exception 'Only account owners can delete invitations'; + end if; + + delete from basejump.invitations where id = delete_invitation.invitation_id; +end +$$; + +grant execute on function public.delete_invitation(uuid) to authenticated; + + +/** + * ------------------------- + * Section - RLS Policies + * ------------------------- + * This is where we define access to tables in the basejump schema + */ + +create policy "users can view their own account_users" on basejump.account_user + for select + to authenticated + using ( + user_id = auth.uid() + ); + +create policy "users can view their teammates" on basejump.account_user + for select + to authenticated + using ( + basejump.has_role_on_account(account_id) = true + ); + +create policy "Account users can be deleted except primary account owner" on basejump.account_user + for delete + to authenticated + using ( + (basejump.has_role_on_account(account_id) = true) + AND + user_id != (select primary_owner_user_id + from basejump.accounts + where account_id = accounts.id) + ); + +create policy "Accounts are viewable by members" on basejump.accounts + for select + to authenticated + using ( + basejump.has_role_on_account(id) = true + ); + +-- Primary owner should always have access to the account +create policy "Accounts are viewable by primary owner" on basejump.accounts + for select + to authenticated + using ( + primary_owner_user_id = auth.uid() + ); + +create policy "Team accounts can be created by any user" on basejump.accounts + for insert + to authenticated + with check ( + basejump.is_set('enable_team_accounts') = true + and personal_account = false + ); + + +create policy "Accounts can be edited by owners" on basejump.accounts + for update + to authenticated + using ( + basejump.has_role_on_account(id, 'owner') = true + ); + +create policy "Can only view own billing customer data." on basejump.billing_customers for + select + using ( + basejump.has_role_on_account(account_id) = true + ); + + +create policy "Can only view own billing subscription data." on basejump.billing_subscriptions for + select + using ( + basejump.has_role_on_account(account_id) = true + ); + +create policy "Invitations viewable by account owners" on basejump.invitations + for select + to authenticated + using ( + created_at > (now() - interval '24 hours') + and + basejump.has_role_on_account(account_id, 'owner') = true + ); + + +create policy "Invitations can be created by account owners" on basejump.invitations + for insert + to authenticated + with check ( + -- team accounts should be enabled + basejump.is_set('enable_team_accounts') = true + -- this should not be a personal account + and (SELECT personal_account + FROM basejump.accounts + WHERE id = account_id) = false + -- the inserting user should be an owner of the account + and + (basejump.has_role_on_account(account_id, 'owner') = true) + ); + +create policy "Invitations can be deleted by account owners" on basejump.invitations + for delete + to authenticated + using ( + basejump.has_role_on_account(account_id, 'owner') = true + ); + diff --git a/basejump_core.control b/basejump_core.control new file mode 100644 index 0000000..3e21574 --- /dev/null +++ b/basejump_core.control @@ -0,0 +1,4 @@ +default_version = 2.0.0 +comment = 'Basejump adds accounts, teams, permissions and bulling to Supabase' +relocatable = false +superuser = false \ No newline at end of file diff --git a/content/blog/en/formatting-example.md b/content/blog/en/formatting-example.md deleted file mode 100644 index 09af806..0000000 --- a/content/blog/en/formatting-example.md +++ /dev/null @@ -1,90 +0,0 @@ ---- -title: "Formatting Example" -published: 2022-10-16 -category: "Example Category" -description: "This is a quick example page showing the support for markdown elements" ---- - -This is an example file showing off all the various Markdown elements - -## Paragraphs - -Lorem ipsum dolor sit amet, *consectetur adipiscing elit*. Maecenas commodo mi fringilla, volutpat erat et, tristique -metus. **Fusce quis hendrerit ipsum**. Phasellus convallis auctor turpis, at fringilla nibh blandit fermentum. Donec -rhoncus -consequat eros vel iaculis. Aenean nulla ex, aliquam euismod nibh ut, dignissim convallis est. Aenean dictum, libero ac -sodales condimentum, arcu lacus porttitor metus, nec pellentesque velit elit id magna. Mauris lobortis vulputate mi ac -tempus. Nullam vitae eros elit. Donec elementum facilisis cursus. Sed nulla massa, mollis id lacinia non, tristique quis -ligula. - -Nulla a justo varius, tempor nibh a, tincidunt ligula. Duis ac augue sit amet dolor mattis pretium. Phasellus eu -bibendum erat. Ut nec dolor non nunc ullamcorper luctus id eu libero. Phasellus nibh eros, laoreet quis eros non, -malesuada vulputate arcu. In non tortor velit. Etiam feugiat, nisi nec bibendum fringilla, massa nisl egestas lectus, -sed tincidunt nisi nunc a magna. Suspendisse faucibus sem non facilisis placerat. In hac habitasse platea dictumst. -Pellentesque pharetra eu nibh in suscipit. - -## Title Tags - -# # Title 1 - -## ## Title 2 - -### ### Title 3 - -#### #### Title 4 - -
- -## List items - -* bullet -* list -* Items -* here - -1. ordered -2. list -3. items -4. here - -## Images - -![Image alt text](/images/placeholder-image.svg) - -## Tables - -Tables can be added using standard HTML - - - - - - - - - - - - - - - - - - -
NameJob
JanePilot
JohnCo-Pilot
- -## Code Block - -```javascript -function sayHello(name) { - console.log(`hello ${name}`) -} - -sayHello("world"); -``` - -## Quotes - -> There's always money in the banana stand - diff --git a/content/blog/en/hello-world.md b/content/blog/en/hello-world.md deleted file mode 100644 index 3f221d2..0000000 --- a/content/blog/en/hello-world.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: "Hello World" -published: 2022-10-15 -category: "Updates" -description: "This is an automatically generated Basejump blog article!" -socialDescription: "Check out this automatically generated Basejump blog article!" -socialImage: "/images/basejump-logo.png" ---- - -Hello world, we're Basejump. It's awesome to meet you. \ No newline at end of file diff --git a/content/blog/en/unpublished-blog.md b/content/blog/en/unpublished-blog.md deleted file mode 100644 index 62cb575..0000000 --- a/content/blog/en/unpublished-blog.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: "An unpublished blog article" -category: "Updates" -published: ---- - -You should never see this! it's unpublished. If you wanted to publish it, just add a published date! \ No newline at end of file diff --git a/content/docs/en/formatting-example.md b/content/docs/en/formatting-example.md deleted file mode 100644 index 6f0783e..0000000 --- a/content/docs/en/formatting-example.md +++ /dev/null @@ -1,90 +0,0 @@ ---- -title: "Formatting Example" -category: "Example Category" -published: 2022-10-16 -description: "This is a quick example page showing the support for markdown elements" ---- - -This is an example file showing off all the various Markdown elements - -## Paragraphs - -Lorem ipsum dolor sit amet, *consectetur adipiscing elit*. Maecenas commodo mi fringilla, volutpat erat et, tristique -metus. **Fusce quis hendrerit ipsum**. Phasellus convallis auctor turpis, at fringilla nibh blandit fermentum. Donec -rhoncus -consequat eros vel iaculis. Aenean nulla ex, aliquam euismod nibh ut, dignissim convallis est. Aenean dictum, libero ac -sodales condimentum, arcu lacus porttitor metus, nec pellentesque velit elit id magna. Mauris lobortis vulputate mi ac -tempus. Nullam vitae eros elit. Donec elementum facilisis cursus. Sed nulla massa, mollis id lacinia non, tristique quis -ligula. - -Nulla a justo varius, tempor nibh a, tincidunt ligula. Duis ac augue sit amet dolor mattis pretium. Phasellus eu -bibendum erat. Ut nec dolor non nunc ullamcorper luctus id eu libero. Phasellus nibh eros, laoreet quis eros non, -malesuada vulputate arcu. In non tortor velit. Etiam feugiat, nisi nec bibendum fringilla, massa nisl egestas lectus, -sed tincidunt nisi nunc a magna. Suspendisse faucibus sem non facilisis placerat. In hac habitasse platea dictumst. -Pellentesque pharetra eu nibh in suscipit. - -## Title Tags - -# # Title 1 - -## ## Title 2 - -### ### Title 3 - -#### #### Title 4 - -
- -## List items - -* bullet -* list -* Items -* here - -1. ordered -2. list -3. items -4. here - -## Images - -![Image alt text](/images/placeholder-image.svg) - -## Tables - -Tables can be added using standard HTML - - - - - - - - - - - - - - - - - - -
NameJob
JanePilot
JohnCo-Pilot
- -## Code Block - -```javascript -function sayHello(name) { - console.log(`hello ${name}`) -} - -sayHello("world"); -``` - -## Quotes - -> There's always money in the banana stand - diff --git a/content/docs/en/index.md b/content/docs/en/index.md deleted file mode 100644 index 5c71b19..0000000 --- a/content/docs/en/index.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: "Getting Started" -published: 2022-10-15 -description: "Basejump automatically generated documentation" ---- - -Basejump includes support for built-in documentation. You can check out some of the markdown formatting options on -the [formatting examples page](/docs/formatting-example). - -If you don't need documentation - not a problem! just remove the markdown files and navigation links for it. \ No newline at end of file diff --git a/content/docs/en/unpublished-doc.md b/content/docs/en/unpublished-doc.md deleted file mode 100644 index cba7be2..0000000 --- a/content/docs/en/unpublished-doc.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -title: "Unpublished Doc" -published: ---- - -You should never see this! it's unpublished. If you wanted to publish it, just add a published date! \ No newline at end of file diff --git a/content/locales/en/authentication.json b/content/locales/en/authentication.json deleted file mode 100644 index b85d9c0..0000000 --- a/content/locales/en/authentication.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "shared": { - "email": "Email", - "password": "Password", - "notYetRegistered": "Not yet registered? Sign up here", - "alreadyRegistered": "Already registered? Sign in here" - }, - "magicLink": { - "checkEmail": "Check your email to continue", - "tryAgain": "Try again", - "emailPlaceholder": "Your email address", - "emailHelpText": "We'll send you a magic link to sign in", - "buttonText": "Send magic link" - }, - "loginPassword": { - "buttonText": "Sign in" - }, - "signupPassword": { - "buttonText": "Create your account" - } -} \ No newline at end of file diff --git a/content/locales/en/content.json b/content/locales/en/content.json deleted file mode 100644 index 02567ca..0000000 --- a/content/locales/en/content.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "blog": "Blog", - "blogDescription": "Stay up to date with all of our team updates", - "docs": "Docs", - "docsMenu": "Documentation Menu", - "closeDocsMenu": "Close Documentation Menu", - "dashboard": "Dashboard", - "login": "Login", - "signUp": "Sign up" -} \ No newline at end of file diff --git a/content/locales/en/dashboard.json b/content/locales/en/dashboard.json deleted file mode 100644 index 1c43102..0000000 --- a/content/locales/en/dashboard.json +++ /dev/null @@ -1,156 +0,0 @@ -{ - "shared": { - "save": "Save", - "logOut": "Log Out", - "myAccount": "My account", - "successfulChange": "Your changes have been saved", - "copy": "Copy", - "copied": "Copied" - }, - "errors": { - "required": "Required" - }, - "dashboardMeta": { - "dashboard": "Dashboard", - "billing": "Manage billing", - "profile": "Your profile", - "teamDashboard": "{{teamName}} Dashboard", - "teamBilling": "{{teamName}} billing", - "teamMembers": "{{teamName}} members", - "teamSettings": "{{teamName}} Settings" - }, - "personalAccountDeactivated": { - "createFirstAccount": "Let's get started", - "firstAccountDescription": "Create your first team and invite some teammates" - }, - "updateProfileName": { - "yourInformation": "Your information", - "description": "This is how you'll appear to others in your team.", - "yourName": "Your name" - }, - "updateEmailAddress": { - "title": "Update Email Address", - "description": "Once you've updated your email, you'll need to confirm the change at both your new and old address", - "pendingChange": "We've sent an email to {newEmail} to confirm your email change", - "emailAddress": "Email address", - "successfulChange": "We've sent you a confirmation email" - }, - "listTeams": { - "title": "Your Teams", - "description": "You belong to the following teams", - "teamName": "Team", - "teamRole": "Role", - "viewTeam": "View", - "manageTeam": "Settings", - "createTeam": "New team" - }, - "profileButton": { - "loggedInAs": "Logged in as", - "yourAccount": "Your account", - "editProfile": "Edit profile" - }, - "newAccount": { - "name": "Team name", - "nameHelpText": "You can change your team name at any time", - "submit": "Create team" - }, - "newAccountModal": { - "title": "Create a new team" - }, - "personalAccountMenu": { - "dashboard": "Dashboard", - "profile": "Profile", - "billing": "Billing" - }, - "teamSelectMenu": { - "myAccount": "My account", - "personalAccount": "Personal Account", - "newAccount": "Create a new team", - "teams": "Teams" - }, - "teamAccountMenu": { - "dashboard": "Dashboard", - "settings": "Settings" - }, - "profile": { - "pageTitle": "Profile" - }, - "billing": { - "pageTitle": "Billing" - }, - "teamAccountSettings": { - "pageTitle": "Settings", - "tabs": { - "general": "General", - "members": "Members", - "billing": "Billing" - } - }, - "updateAccountName": { - "title": "Update Team Name", - "description": "You can change your team name at any time", - "teamName": "Team name" - }, - "inviteMember": { - "title": "Invite a new member", - "description": "Generate a one-time use or time-bound link to invite your teammates.", - "userType": "User type", - "inviteType": "Invitation type", - "linkButton": "Generate invitation", - "linkGenerated": "You've created an invitation link, share it with your teammates so they can join" - }, - "listTeamMembers": { - "title": "Team members", - "description": "You can invite new members to your team or remove existing ones", - "primaryOwner": "Primary owner", - "updateRole": "Change role", - "updateRoleTitle": "Change role for {{name}}", - "removeMember": "Remove member", - "removeMemberTitle": "Are you sure you want to remove {{name}} from the team?" - }, - "listTeamInvitations": { - "linkDescription": "Link generated {{date}}" - }, - "invitation": { - "invalid": "This invitation is invalid or has expired", - "title": "You've been invited to join", - "accept": "Accept invitation" - }, - "updateTeamMemberRole": { - "role": "User role", - "buttonText": "Update user role", - "makePrimaryOwner": "Make this user the primary owner" - }, - "removeTeamMember": { - "buttonText": "Remove member" - }, - "accountSubscription": { - "title": "Your Plan", - "description": "You can upgrade or downgrade your plan at any time", - "updatePlan": "Update plan settings", - "billingEmail": "Your billing email is {{email}}", - "billingDisabled": "Billing is disabled for this Basejump project", - "billingDisabledDescription": "You can enable billing in the `config` table of your supabase database. Alternatively, you can remove this menu option to disable billing completely" - }, - "accountSubscriptionTakeover": { - "newSubscriptionTitle": "Select your plan to get started", - "fixExistingSubscriptionTitle": { - "incomplete": "You need to finish setting up your account", - "past_due": "Your payment is past due", - "unpaid": "Your payment is due" - } - }, - "newSubscriptions": { - "activate": "Select Plan", - "tabs": { - "month": "Monthly", - "year": "Yearly", - "one_time": "One time" - }, - "intervals": { - "month": "month", - "year": "year", - "one_time": "Once" - } - } -} \ No newline at end of file diff --git a/i18n.js b/i18n.js deleted file mode 100644 index 9005398..0000000 --- a/i18n.js +++ /dev/null @@ -1,23 +0,0 @@ -/** - * HACK: Known issue with importing with the newest NextJS version. - * Can be removed once resolved: https://github.com/aralroca/next-translate/issues/851 - */ -const workaround = require("next-translate/lib/cjs/plugin/utils.js"); -workaround.defaultLoader = - "(lang, ns) => import(`@next-translate-root/content/locales/${lang}/${ns}.json`).then((m) => m.default)"; -module.exports = { - locales: ["en"], - defaultLocale: "en", - pages: { - "/login": ["authentication", "content"], - "/signup": ["authentication", "content"], - "/invitation": ["dashboard", "content"], - "rgx:^/dashboard": ["dashboard"], - "*": ["content"], - }, - // HACK: Add this back in once resolved - // loadLocaleFrom: (lang, ns) => - // // You can use a dynamic import, fetch, whatever. You should - // // return a Promise with the JSON file. - // import(`./content/locales/${lang}/${ns}.json`).then((m) => m.default), -}; diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index fb01450..0000000 --- a/jest.config.js +++ /dev/null @@ -1,48 +0,0 @@ -const nextJest = require("next/jest"); -const createJestConfig = nextJest({ - // Provide the path to your Next.js app to load next.config.js and .env files in your test environment - dir: "./", -}); - -// Add any custom config to be passed to Jest -const customJestConfig = { - // Add more setup options before each test is run - setupFilesAfterEnv: ["/__tests__/setup/jest.setup.js"], - // if using TypeScript with a baseUrl set to the root directory then you need the below for alias' to work - preset: "ts-jest", - moduleDirectories: ["node_modules", "/"], - testEnvironment: "jest-environment-jsdom", - moduleNameMapper: { - "^@/(.*)$": "/src/$1", - "^@tests/(.*)$": "/__tests__/setup/$1", - }, - testPathIgnorePatterns: ["/__tests__/setup/"], - /** - * This is where you'll define node_module libraries that need to be transformed still - * By default all node_modules are ignored - */ - transformIgnorePatterns: ["/node_modules/(?!(@supabase|jose)/)"], - globals: { - "ts-jest": { - tsconfig: "/tsconfig.json", - }, - }, - collectCoverageFrom: [ - "/src/utils/**/*.{js,jsx,ts,tsx}", - "/src/components/**/*.{js,jsx,ts,tsx}", - ], -}; - -module.exports = async () => { - // createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async - const nextDefaults = await createJestConfig(customJestConfig)(); - - // Next forces the transformIgnorePatterns to include `node_modules`, but that breaks b/c of the supabase transform issue - // so we need to override it with our own transformIgnorePatterns - nextDefaults.transformIgnorePatterns = [ - "/node_modules/(?!(@supabase|jose)/)", - "^.+\\.module\\.(css|sass|scss)$", - ]; - - return nextDefaults; -}; diff --git a/next.config.js b/next.config.js deleted file mode 100644 index ce4f1e8..0000000 --- a/next.config.js +++ /dev/null @@ -1,17 +0,0 @@ -const withTM = require("next-transpile-modules")(["react-daisyui"]); -const nextTranslate = require("next-translate"); - -module.exports = withTM( - nextTranslate({ - /** - * Looking for i18n configuration? - * Internationalization is handled in Basejump with the next-translate library - * You can view the configuration in the `i18n.js` config file - */ - reactStrictMode: true, - /** - * Adds support for MDX files, used for docs and blog - */ - pageExtensions: ["ts", "tsx", "js", "jsx", "md", "mdx"], - }) -); diff --git a/package.json b/package.json deleted file mode 100644 index a1c146e..0000000 --- a/package.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "name": "Basejump", - "private": true, - "scripts": { - "dev": "next", - "build": "yarn sync-stripe && next build", - "start": "next start", - "lint": "next lint", - "type-check": "tsc", - "test": "NEXT_PUBLIC_SUPABASE_URL=http://localhost NEXT_PUBLIC_SUPABASE_ANON_KEY=anon-key jest", - "generate-types": "supabase gen types typescript --local > ./src/types/supabase-types.ts && eslint ./src/types/supabase-types.ts --fix", - "sync-stripe": "npx tsx scripts/sync-stripe.ts" - }, - "dependencies": { - "@heroicons/react": "^1.0.6", - "@supabase/auth-helpers-nextjs": "^0.5.1", - "@supabase/auth-helpers-react": "^0.3.1", - "@supabase/supabase-js": "^2.0.5", - "@tanstack/react-query": "^4.14.5", - "@vercel/og": "^0.0.20", - "classnames": "^2.3.2", - "date-fns": "^2.29.3", - "gray-matter": "^4.0.3", - "next": "13.0.2", - "next-mdx-remote": "^4.2.0", - "next-translate": "^1.6.0", - "react": "^18.2.0", - "react-daisyui": "^2.4.6", - "react-dom": "^18.2.0", - "react-hook-form": "^7.39.1", - "react-toastify": "^9.1.1", - "react-use": "^17.4.0", - "stripe": "^10.16.0", - "tailwind-merge": "^1.8.0" - }, - "devDependencies": { - "@tailwindcss/typography": "^0.5.8", - "@testing-library/jest-dom": "^5.16.5", - "@testing-library/react": "^13.4.0", - "@types/jest": "^29.2.2", - "@types/node": "^18.11.9", - "@types/react": "^18.0.25", - "@types/react-dom": "^18.0.8", - "autoprefixer": "^10.4.13", - "daisyui": "^2.38.1", - "eslint": "8.27.0", - "eslint-config-next": "13.0.2", - "eslint-config-prettier": "^8.5.0", - "eslint-plugin-prettier": "^4.2.1", - "jest": "29.3.0", - "jest-environment-jsdom": "29.3.0", - "jest-fetch-mock": "^3.0.3", - "next-transpile-modules": "^10.0.0", - "postcss": "^8.4.18", - "prettier": "^2.7.1", - "tailwindcss": "^3.2.2", - "ts-jest": "^29.0.3", - "typescript": "^4.8.4" - } -} diff --git a/postcss.config.js b/postcss.config.js deleted file mode 100644 index fef1b22..0000000 --- a/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -module.exports = { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -} diff --git a/public/images/basejump-logo.png b/public/images/basejump-logo.png deleted file mode 100644 index 86eba36c3af85fcd68527a9bcd83c75521cdb879..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16809 zcmdVC_ghmz^9OnoK#GMTDoT|iO+i#ZYGR|R^bUg3qzi&bO;i*My$VQ?D!oW=K~Rc- zNS7LsPG|xlkc8xJe82De%l!xL<#~KK?3tbY%Yv<_@5&WWmd|lI!3H$^y*}VpTGe6QWdkjIxzS91|;w6u9gT~X|w@kea zJRH1z?|a!pzP`RvPVO#`ZSOy_m-6s(Oj%bx13?0i=FMw{Pd}_p`F*f5dbUe)Of@iA zVAE*jy3Qo<{C0d4Jv#>nk~0MAKY4VIxSLa2<54s2*3A}o{j>#Xw~Dc$YQmj8)L8f2 ziD;yVfQAPAlKAEOuP)jbEq`blC8^ur$oZs|`gpf&cas#Xy0PdMGc=0N(e^y^s=(Yu&hZv zA7hjnCj>=nY40(L!CHO-W`eQRo2z|@qf?Lx4^TOaVxEz z2+&+P?=r)mK@R~oN54DM*oY~zK4s_;62;$P4rg29`>q5|~t{#m$QV%>IDSSxdp23uC$qFGAPsoxPm!7Dg zTUJGQ8^*QpL26G>lDsxK2jM+GVt6H;!lIv43~*^)8Zee@y5Ll3S8X~buYD;}dzV>q zJH*M|-xXiQTUvi0fBg*C|69pNjCVF`^ws@-3~2LAP4}1w&RjEIxN5rw@8S`1(*H_$ zYEa>8W~&&!%Ii{sA?s;=3^o=F@Zxvpgfdh^P+KzyRIvKsf@Y9t0(rnJ#>y^7Tyq+$ zffM;WX@2lWvb(0DMJZimCapf7Fb*%EE3@!MBXEUy}+*W=2}mHf;EW!@*pDL%JvB$mok}2VxNN3;hf~`EkD% z^1}=QXL~BBCoY9A%G;17TaDV%&5PBH&>go=*dVfY-pb%CG!E3u=F?}!?hUKX3|P1d<8`mZM_+X>#colj3a zNeNTs!B<^&z3-8~yxW#&d2tMnEVJc=1Qwl-QjybINXr@2-g5VDiJ=5R9kn-5a6=`x$2M69~Zm0l(9*atN5F}+G4 z;Uz{FH?6Sz--aLVu@3nU=+$AlYK3wXw6WV*Ymzu`!+ZUCsnbn;-`-zZ%CGQsy?peAxn)>2iQ~j+YgwZc(m*kD2Bqyo6V1MA`j8@g5qA)A*|u-?c=O@v)`eD#se% z0ZDFJ&hU$QF<*V~_m-$d7RE~@f@kjm&B7P_*_LRd2&`|ia+cFqrh83S##EUULMge& zZL}(=vV_X*JRmBV_XUv?lxKqRXo?Rt*G1%xmr6oo*}&bj3)GMUXk8CNAJJAM)7=t> zWf3K;W4yC*o^#={$u1*0luVo_CocokDP{sd++@C3{}a#V)lHC2^4UsUXy8+ergiq> zr96Yjp=C-Kqtu&CjB*BsjsF%Rd{hzyvp}?qO#tvR=moCdtJ1lbV^Lwebs(<(ft)i$ zwvqzTH0rF=KUY`v$=H9+B%p>Qi!HqObU+g|yz)@vC9sf7g2go7W^;AK+=x_f*YuqY zvslykz?|~sOBoB8YCGgVkK1B1LLjzb39qP^wV~2rr&QFY|>6dM@FsNYH zM{|UKhLu>%-Ck{-*7J!i1A@d8s%Y<7eh{}LrnU|h6E-%r8&?(|@UXvXi97tD%6;mx zT+gUZx@uM#KPGv2lXYj>tFJu9SdO;>TVB$k|IT^1`7g=G!q;zgm``QbIg+xY6^T1* zAF~m#C7~jOz_U86{knnJ%85km;XEkk)1vQR1X5o&W2GYBYPU$nSh?7bB!pQN#+W@G zijO=0{rt z%Qv(uS6gm1X;I@)oJAM*pG;yk>aQdpexMFU(wdVc)5*l4U$q8{iD0}Fj6SVNytFNj zH(5S*Fhrd`Oj>*mk75Es!2N&_uQ}MvezATr9ec@cRmc`tI)G`zs#zxoV$;j*x?;Af zM(=TZer~bhgj&ioLr@v7IM^J2Vbn?a)(WOudF?(2%H_?{rDuYRQ62K-IeGO2)AP&q zd+zWzaOkR0<^QSM*}cADxh2TK_0(=quS;)#dZ0r8mpC#*4<)iis~Ia=s!yt;=#sZY zZ^2#zbA6QkX@-BPY5B*5x$5??wq<0SX#>)g%AbH&G2&JjS2--OX`>a`$S0qK(E*!2ys zbuPlYdEOGnvzlvp*0m|wnk60462@TCw)|@pnW=8J7u5szv_os_{5*9bG`*X7F5PxxuXc^}RSuc7Wrj35Ukye~jB;6B$Y1q8 zAK{%XP8QKLW-KG8|6Hdin(aTfiafgkb}1Y-eQq97iCT!bD_`)YJet;f+WKA6J4et} z#`mmi3W6*QPEcZmvx2Nx|3h9jk583K(9=tQs4u&Qh^lhH2;PNEUFJ7rTR@;Yh)ZOfGA&1 z=&QuqqD`#sRwF8UXvLrBSoUrg)#}Ly9J_Ga_d;W0`knzF8D`N#b>ILF??L5$&#mow z+|AM1M*_n-6uP+v$W#)Gi!~`0bGgi`R{bf({`d_T^k)tiIC|refaHwyd=+GBon{i9 z{^d8TsmoY4-(?nvE%rhlFY0~#h4Deuq1=O-*g_u%mjkyHl0-=;N7@Y9VG>yKZPzY} z$EdA_X+j#A?m`J88-t6={;-znb3|0{n|HUtj!LF%dr{skI-!MbOD!lTM_$3cSw%O@$tpGUE2~#WBrldz`4k58of`YD({PaB~GO6 zs-*3*9|g!5T^1YouEUwAzwqhUntSQzbx5zd7@*g`+C@a~#w_x#`t!)xjw7V1sl|aK zg`MV+ztvp<*soe?DyI9v{U>!OGE0b7lW4qTh$_c57$oRWj6!#9Fgvs&v?f$ju%*U! z#T))t#JIp%;o>t`$R)@OBZ0Emq;mo)&rHmNBlkwX5T~nKN-J_Bhq536W88?! zap#K1#GTXoQ+E?#?GNK{GGDuz|85>{#uFk}{Cn+}6Z)DmyJlyPqOgez!L~X^O^;ld z9s8yVq%1EUCld%O?J-h~o{OKPm)`uT4S)5|W~h=!W1gV|O69xDH1T1IyPvmr zTw-8{OUieDC)+*#sq~b;8L^4X`yK~+lvu=Fa`RJZ8XD|-QOn4)=T0s9oU>W8lE3GA zdw=>xZ;F0g&+0X{YVrz zTL>;D_6ospnG+6^1M7vPp&P6@QVZ5O(W{q?~6S_lc|j&iN6C$+kLkmfGTAt|5e zf4yQSG9hZFvnAIw=WygHBh9WuTs(rb2ZX>>eYTWhEUH^HiR^ser`ex+w_!~u%lm-8 z{iatjwKgDd$)zWaoSUq5jF>OI-!!1aANkuThM%rFmedFP&7<(4tlFTR=Z`;^=XfWSceiqRtlS(7_r0&{dwx5b}uD$n=W;5zS7VSIf$YQR<86l6NcqrS8rHBA8?j z=fR-GSlh~crODh@+q}l2RCE6uR$1=DXIuI6k-%MD^nZa$$sVwY=HDW_}BGk686Db(6rwSnHJdA<8t-ikJcofLrUyJjwaLT~~-#V~W&GS>gx_+e+|1sk4 z(cLvA-vR!6Jo^R*?tvF42Qk8_^^2E3mdfzB)ZE$M4o>gsI#`5i+ZWkd61q|L${Y7J zXP$W4_kFYYVLFsB-^X0mK<-td5{9g@-}M0pK%{I4?LW2dOOzrGH`gy5K77Tq=jSaO zTa)y4f8xoP`!-!kvvMm3$EV%*aK4?3`G{^1Js-%;j9a9~?A!r?=&Z+O7=IwPMA7jJ zEZWNBpxxbSCu_Lvbvp$a%rKmQ`7Cib7NXH)B8hbB=hTE@H~vyGRPh<6dIg7H`Znp zly5ez-Misxhm|}2^YZ$sZlyoe#5bd&k!krI){_}vzRt$rii3zg2cv6|E(A^6$~vXV z&?V`#hDbwq1E&?sed$uyr86V$7C2xBiNFqcxCq`C>*fx0znsb!kBs{7UEi*80vi@pwrye=Q#oc|W;w9z$VJ#>6; z+AVS!;XyL^A`o?l#Ne)@T{g3z`+J_)M{-fGKRjAW zQ{4V^x+L_YkmKEv$}id+IS%ox2hVR${AdfFQZk@0r|OAEVlO9g-!}yo%n&01%h9wL zx)czfy?DlABGP=UV7|;foVc_g?L(a}$9k%KC~JQD{j8!LQ8>@mRM|KEENZIqw(foz zCfx)bhy?C@542SC#K)5%Ac?^b5VY@8nuNI#!7 zIM=+%!V!}PLV||8dg7f`W~_~-3G_+K0pG9GXtm!oVM$IlN~?@ajwEx-@&}!sNf&G^ zgPoVksf8yXj84g{*Yu z%#iN}EyF54WQor$QBB>}N%h|^F8TCIwg^RDRp!MvTiIfC;*oNm{c@rm3-<9JPoB(d_(2zNN>m=n_wia?|hTqg{=txj@koizzys)f% zy!ZFjrzPQmv`mGJK69tb9wRUy&zGp*-S1cj!}4bZG3(D?JQ>X{cZ;?@wGZ+wm+Wde zq6nL;Uya~@j-fQq4C{}a&|bW`JCK1S^;iiu@x6Tkk`g_?IdtPTS#MgwW0^yvQMDnV8B ze2KZ=2F_w_Iug~(nsq9mwDEdrRoz89qjn(;6@3jYS=P^0Sa3t>e{dc+%gnp zv*=k&B%1X%Us>8f{Y9twxjnWUz0pj|@|-1y8oMxSfE4&RA+?B#3!xKgxaXUmw;lYb zw0hT2u;JVmNLSg>&tPnY;?b2rgDBn_OoYnAn$8je8E4_$r8D~7VP-Az5lC>`K{M7V zGg?UVHf!K~h~GORfAT3szFAF{&Cn zvPKrX>_*9p)~W>vWs(CxM*)VwDMaQz9)~_3g0jI(om)Dbcatz6&`0o@VKrF}zoQau zb$p*=%~5}3W_Q8^QDG_PXzFrGYy8H5q2|kg)Zyn0hYu4r!p7MsGjVzNUc7a!JL{TH zF7D&d>P{VLsechWAU&$g--Vx^oGdJ*JguVaPqG$R{_$g-&?bA_baY(&lA79$GLUeJ zuiEX785dbAfWJaqu9O1$@2-r_@73or%bFKegO|q>IS!_k@ky&k;$d$ME(3h330$!z;_%U=k}Xd^`MOwUn0OOLN|5P z0Bh^ConT&7+e9#vLxtQv-=z6ouljdZ|2o{VR)Y-|cA>OvGEwd9^XcQXMXV^xW8?OL z765ebf%Fp52q)OUS0T9zw+ZY0ahgZqeh7x!Oad0$UcKz%*c-9Kg{At=z90_1YsopbN%@6QjF)b?S+XCdl{3|Q_O*?<^()D~D8tviEBSwyWMi$z@brYwz5X*!} z%*iplvqP2Ye#(OkVU`wK=1DpYl~%;m?TOC;4)@R5EV$45P?kg0pQvCHn~AZqG#A-f zVs_Z2H~FT=Yl225G#L%XG87_zJ}cP`6Xh*WRFw`*lv_ZGv99G z?y3WqKAa(A=Y~uCBvwMeo{8$(rNzA0@GW#XDBxzzw-uT(*97lBvK!@oR=Tj#q&aou z#=9oyylL5Q8QY#JPb{N`P%9#(qD(X+CqG-VPeK26Fd~m zo&db#0hI^R+zKMq@j8t%N_iU0cIvnAO+5=Kx~<+^ri%ZlTghcr?JTO!Gts|pai=Y$@M6{ z(QkSUU+*zCKALG&3c`M{341Co>lMD)r@qJNZyn$t*r$@1O;o8*CvB2s9Wmn3RvRzn ztCwP^X53>%t;mrjL~jx1_x+0u9FkCs_oQdXl$C@m|G76RuuhRygf+-52CU_KI;Am- zozBu$RnU$V3UvCJiVD1isK@1ZmW=i_8x>UFWB+@rKjY?Mv@q}E9+^^U)3VLk_9fex zAdzJfo#$Ed_G!wqN@>Q`E7e9O=Toy5a+iXOP^3}R&_Ul#!kZ>FbB74xo<+13+ehJh z5PW-4y0I=lA1`3L>T4Aad(ASFzOl)g9i;G^EZge})x;wbmywqbQ;*VD$ z-jX|os{hpt7M0B!v~ z{vUVg8lCJA_f;vL(gmJpmGc+`T3n%ux0}+&dyE5(JT_tdx6-d(AIuATx0=j~AS6(V z|C+D|v?qV=`S3YC+!guyrUVQ6E9ersDedsD%fUaB z@5j=cMJiGRPv%QO9H0v;=z@)|{PE8%&kxikU(J=RaG~t7O0U!hq@5;rr-ZFy-wUNv zqLT1Qc|r}RAH)*jY&LR`6gwpZbzwm9cp2_)*3Pg_Wg+K`4jI+Be^#Y-{f_3iYffR< zXnkbT<6Xp=!7n)MWx>k~$Om$eI~yg$B8RW(zqJ4;6~JTj3*pYN<4GR2uBtGZWEmgw zf)anwJSJQ=AYt*mO1jQoH{Yy36kd-jX?U{LI4xN$yI9Ys&2{+X`JPom1``vi6{B~- zz|q}ZY30w-v03$&8sl_oUxN=ewsdr7G3C3O+l4z%CO<8#{IDIDyUQ~{9GLrCoO)a> zv6Rvs;XyvTFeB)Y#F}oFci|ny$B5bdahrV-m2d9EjLu%@u%Zw4PO-XASR}dC*f*sD zd*31bv+|b9V#vXi&Xbzn;d(@^O~$~7oTJphU1O`kv-!o!j?4S^+u8DNgQ5e?vtS~{ zZc^Sn1D#_~flliKSe$gl@an!=a{P`8V<;6}o?StE=SF*X*C!r&j$lz2_WL{o zZbsTUK%(d7-f>D7S2;Z!Il2r(>ROL^qP-F<^b#fDCW3~eTWNMCeAEoiM0qGu3o9D| zi!UGW(|s6%nAvDWvmQ2g3QN`5RF}*@ipIY}30(NsOOVWrN?e_2nysf$RswpX9l^C; zLZjvbI-9H+evKQ<8Bj))1UBud#m6=j=gfxwtVkrQ@1hpd#bNz#1L$lFXZRnP!Nnl4 zxbK*az#PKJL^{ahn>^j}m3+FzAI8DRj`XCo2<2BFiwc``N6c*?CQu-`L1o2{VkC+^3)0|HQM@{HZzlS#8I?oC`w?N z2Jx(6Lm{SIr76oQvW)>yopB#WU5!Kk6r6(I8}2d2oCnP2?%^jI0*A1*f5kxR6?f#`=Rba4b(wljki_hF@foPsQy;8zR9piWtjZ3oAr(Ls zhbpO$iiZ7CbgXy!ejJKYb%lzKGeC8=n9rmu6B82){yd%!+R`mFbx5o^e@Nq&`DBd8 zhx*@yyNoe#SIG4Upf!+*|5nn$CYpONs@i8@QYE3(s#MHkN#g+l9(hiLDQmHf<_6sTo3DVr zEbo#m%zTUrR`BFxOm5%yVhQ!vbL_XPMTYn3Y-Yj4@w>DqyDuM%-xEg zZmIdN@?Re&AWYvUL2{5>Q5POU*Tw-lzwd^lZ4ciUKOOhpxKR?SXDZc(>DwM!G&39K zr)=EILMhlJaw{#~x>`B(x7ev~D%tw_dl-_!mGVyA0h8B(XJhF$+OyD{GSX>EK1LD+#Hv!lXeK0rK8ik?2??OW?mbos5qrvs z{#vKFa%y#pe<8%0ezeb#*@n&rBqin|`wdD$*%&aP@F?L^>IZ>9a$+7?=>1>pN6WNh zfJlr+)T;@zIV2z>m3vUwY-D0>lotNvJF0`wGrA0^!Y=Xv)9EngPl%h;8Ge6}E-EZL z@1Qt)x*%`eS#~{?a(3QH0l=97wx_f(7m7{TDZ_b&+?Pyq{l>k|Di{Z-RWPV5YwNGv z2P^Se96&lYpytAOa4U959hyztsYInO<~OW-tL`toXC@89>6n#eE)rOcK<2v}P>N=O zMDBI2B5H?ZmlF!xO!92j`>+-%yG&ouG$(;J@Xg4@GY?DIMd{xqlfL_wo?$qA- zzV3;PfZ9d7oTzUX3k&t#mSzKEW2y#W*Y+5xjraG12J1KcmJ||gze@l+={j$!7vIxo z6`7^y3Wcfb4^l?VeN0sFPw#zC+Ye2MAB4SVLzMm9CG9$#%K&HBxkL|4;V&JNju#^6 zB@tPgby^hOn@e9hRRcCxM@~^-33My!Fr<5u0pC;If#Q;mekKPfGrqGD6YpOgl$n0# zZyYruyNW)H6BJ(|gMgs&IBFB_?9e+QbidZ0F3bvP+`RbHAA4LrO3X(k5x271N0_A)>c8old5=Dk!21xqdmq7 zV|WY;>J1$I{Nm>kW4jbp5vMF%d02=Dx*fPBkK!S#;@wibEkepEmM8B@^~3>|d?me^|gqQX4riBdrJNUR)3& zV9zgq)>>wy_oOeA@w`nU8Ly1*4yAv`oIm!}N0C;jl8yHU*^5LFoCLP}8B3yNtvyvD zK0cx^3ywzYPf+9uK1PyTER-Qc+$SAIVNkVFnR@Ke2?_@T#9)87nR>}A6{Oo=lvAwJ zKc$mZu%ja@)$)YsgSNNM3IfZ3y}bf0*%9OEGyADW(d{%fDK~zj%QdhSFZRklMQ!=* zE$(zP?@sN@mXRVqw2Due; z;vQNXo=IiaxUr|b?f@TPqe zn@Bfs@;|8b>lrirJ{UTm*{ZQjfFht{(yn_Rh^amUl;tZ)m=mb+uoMvp|3B`j3@z1n z$7ePgBw$5U)(f}yEL@V-b$)>y4XCpL=`O%K=E{yO8(YE#o{EVpzmt`nt0zRMJ8aCW zc%wPZ!uywYfat~ZnvD#P#Eyny;)#LQL1}%*F?2y`2;T$o!^Rr=39X1SnqN5gU3pM$ zgYIJlQ=S4@!~tSUjN^k{5mG*2_5QMQgK*dD9B_COE?3Bf&BG(kx0=!>mMIGrX8m?O z7k(W2Z8}WsZ~*Bt?u620c87BUYySjDyut_Ld9+h%tSQAlsMv84o!?#9?mAm-6P~_<{oQUsL=c zsLj4Ov@1wQ+KG@wSfD>K)k5eto`XW8NfERV`e20_C0VG79Zk>@k?kNR-*#%)WW8PI z_f7qK&Vbh`+v`uOf=zW3TCIW8lyXvn*S^R?RlWU857tHVW~n=AZ+f~QG5M5|d}8lg zae3cnQ8Jx@$BJ@V5_^Him`E1T4{-@bXDmxuX~$y@h`6TZWiuVctcQ6dTex-=>;6!nX}Aflci2| zK4HWxHh!cfq7_8)%A7uf^jX%evFK3apN%N*-9)?Lwa^Wyzjr57K`GQtNf8io zX^QdJMo!B^ay6?W8_@@(^gWU?K6~{>pjymMrF1uJIi|*m`b-RZZF~lV(5Jxo@NsBm zY1oyj)tqTYLV#ldR%ibV)ux1od*E!}KiuV40B#b%?R){|mkKle?3CM#S;@G42hTc3 z3`K~vTo}btg>@me3#01LaYXdw>Ed*P2g{gfNM5*_N06|mdk#0@3yg_b0AqbtNC zPNs9Z05db&w%0)_S8N?bnlBtiM}s0ux@u29`il&}oMay<2(W5HAb^7-oq&r=^D6Ue znLTGU%pgU=-?^4exZBp~RKTo*x6*66kHgY5{$m|%CC=N>%v%^4KD(n7CzuJe{ zTR3T`6ZSZ|dG^IISwW&?}K1^hqhOgB^S+ciUmO$&ENZP6ciVYf*@=@)_?cKt8|Hz%N^ z6NL0e%4Qz@(7949Rn5K`QhsF6(aq5f{ZLSZzkdm|u0^^H>S_63H_*=o^(Lx-6Aobm zC3MLe67K3G_u#o$`o6KZ9N`5^-`o-0chEvXh8RtCT;_VsRQ&f_FtK=_aGVThD%dZX z(G*hYcH}nLK zok@eaBe_Dr7}tqGF>@9HIe_UuLYmJlNu;v`_5hYRG1;S^K+kzVvVJ$oi6(h8l8w7c zo~T5$YV9%EjDQ9BefTySx5;|N9B#V3W-_D#3#7WlroaU>a}6oln$2MNyRGWbwinHl z_t9sAc#BVfa)WA2D(9hQYE`!p%=F-0X=%a^wxmPvDle z>Gmk{=X@EEG3yP>6|ym^Lc4UrLiu1E1Q_R&HWGYkaIuztvkjx_CkZ6xPV5E}ySUe` zipHpd9n;IGA@&wH&4U{P)1TpI(t;3V?$|_*76RhK$efjL(aEu79 z@}sW%W2A61C}S6bK{^JXa9|ZFg3Fz3{3FzBA6qvFv$u5@BDZx10iDUpTRsUd9ngzO z10~6mnoCSJ>|h3023e&~8};2rjrQP7$QJ zn}%Jr9ChmRw@swht?4<@*T!`)kL3N#Gh!4bJJ+RMc4Mz-4w!1%@~}$#^t6jUP+_p? zxZ_Aby$BBM)TvJoXbY>~?7v~fnsYV;rP%1X@|@ZMR-B4U-Z@W9MgKY5@6;>vxk}A3 zUk~1f!P&BY{FqP@uDSs7;DMvIT?M-a(#m~Yk8^OIcEhSxeeUL?6PH2x`^vXU6tM{5 zHzR7eCB0LGDy!;u7m$y!mWt;~u&#gbUD|`{6gS$wDF(@mour)dAaxXq!NRlXLt?8q zkb8M4sR+zk@!y3FjF6w%=|O^5uG4l% zXLaGNnl8)&-4(>>f5(b2x7roBu_6Kj;XNCqsYBvD5ndk!27ix!hRN)Pt8EKb() zF{0b!tXNy(c|xdf`6E*@os#W2fbSx z$0^8HCn}&hG1|l`f(vFg3R0D`1e&`@YdG_thG5PDXznVlfmeXsc9#4sQ2aJb3`v82 z<39~#@tZI)U0MU(|M8e_hB#?FAa=@qb_z0hz8>NPEM7{%LQq#j5K$g=7oZTT5(~&! zQy(#*1gKtS5EGRkW(ZV68{$Ey6-y6_IB^ zZ@JzX9a5j@h1y9Q=bJh6iB{c>lerUv6*a@+H}xYo z8Kb%Y26ZU|k>9DC{GD;xx&8XMyO-uiC>*~{CXZa4XF@^xhYkZkDYxU$XWLK$w%J~V zLyFgO?Omy*<;t7KTKSOz1^{=YRe^TP>lp-BH{HZpGVDlP5@0NPE?y*KeUtz7Jgk+! zCBQBdVxzp#HezNvL7_Y0?FX#)V%!iA3Y%mYhwf}el|4Vt%0_`+#jb$PN{f~BUr$o# z=AY6C%Y^c20y$q5hrJ1*A{8w7zzPl%jt4{Qq~6p<{+U6TwkI+Iyj2=BJsU_ybNp5& zGk*WQ1&lw#I9~$toh&C8w!a65o`*x=BQEQtK~Pg-4%Y`@?Coe{=td54qEAS=TMH(k z;_WOWbJ!JvxA@N|a72x7@}JM?rVBx!{c|#*B1^xCXCuj24d*8+32L0byHI5s+DM01 zR0ih-8vZkSUMk?8_F!N>X52C|i=9=qF8G@)COG@iMpn&8ntZ_DTPClX?w+Hm)6Sy3 zJ0j&=c)C>8-Bs&L8cYDJ-VaAeQAvq4VYMc%DW4iYlH3=#og5GlCiBjcwLMz*GAxOe z`i0{jO$8zSbNY~%Lp zCtp&Lxti5(j`=<@x9vX#Mo@aCG+57cH0GZymh8lixoE ze?AyG+$ZVG93^bqV^o!ZwhX0S3>%%YrYFj2jl8NqIm0Tz;v@$3X+M!3o&ELfiw?Oh zlRnH6{+5d}QErqp9j?DIan$XMFd24Jgn{ArE}tgI3Ce`yj+*F}k}z{yIBEl4YyC7H+|}Trqw!TWcum8(a3nc>ZMb+ zuJ6%l+?8;E;HjypZX-mjteW->%X6|o!}rfY8@Iq);{p~m*cFy}&GVuhikqdJu!_x< zgXWLl6v|Tk_28RQV7x7T6A11yQsgvU=`_fk6GN5h>WQx`tV$Nt9QiJ<=ApIVY3rRQ zGfm|QSpCbY#OF071{M&kHf9{I%Lz{HK-5|T@NsKq!2I8a_31SkL-(vO27jNegKV7*_zsXXDV7 zfUzB5Y%2zH0*pb-LLSnC56~E^aH7L9>9c8!Q^cUtYRr$qL8}#L&7!Xb3hk6*K_1hk z6{yg4sA4tPq zuv4J7p>SQ@I5f>iy%&QR)My(M&QlB-zuyAGM?v4zU`QA(+=B0zH6-+DZ;_dh4cdec zgIiZQrrfS&{@ee&=S8(srFAn~;A2TfC>zEAP5AAXHyE!6|1S5?AeITw`YW@S8}Dw6 zOg`EN*AH;WDLRpV^YyRjJSmG+ZA5}z3a&h4$g$|hy`Wip@Esu=q;Z@DdL>J;?T#oH zAsz2pUH3m9UZy6kN&H+jYQ3v8Y70YGir4W$11|VGiBnEZ2{-KVlB2dxEnDaMs*h8k zi&q>V-NTo0L+_deY%x+!VYn}0Ht(`ai|S@7JTxmx80jFVZ|9IhbMqY=jlL_C!x-yJ z6i&@hv6u7xT9_Se#eE9+fOr^e8U)t#2{(9_eZT&B7a?gz^3hbU16%X1{l5UN_NO!y z_4TzoU3SkTkpf+mx<#n+1S{0HrU#Gtp%5@huH!n(TyEcog^J5JE*q1&Rv?$kyy$k# zWHG1JYDE35fkTpR2|LfD)1qW`-p3G34Gj8%FqmEbhSURX=>I1_0`U}9UaC(gf%@8o P23hmg-J2!XZJz%>J_R4S diff --git a/public/images/placeholder-image.svg b/public/images/placeholder-image.svg deleted file mode 100644 index bb23d6d..0000000 --- a/public/images/placeholder-image.svg +++ /dev/null @@ -1,720 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/public/images/vercel-logo.svg b/public/images/vercel-logo.svg deleted file mode 100644 index 8778286..0000000 --- a/public/images/vercel-logo.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/public/robots.txt b/public/robots.txt deleted file mode 100644 index f1bdbe5..0000000 --- a/public/robots.txt +++ /dev/null @@ -1,3 +0,0 @@ -User-agent: * -Disallow: /dashboard/ -Allow: / diff --git a/scripts/install-dbdev-with-test-helpers.sql b/scripts/install-dbdev-with-test-helpers.sql new file mode 100644 index 0000000..130b673 --- /dev/null +++ b/scripts/install-dbdev-with-test-helpers.sql @@ -0,0 +1,51 @@ +/*--------------------- +---- install dbdev ---- +---------------------- +Requires: + - pg_tle: https://github.com/aws/pg_tle + - pgsql-http: https://github.com/pramsey/pgsql-http +*/ +create extension if not exists http with schema extensions; +create extension if not exists pg_tle; +select pgtle.uninstall_extension_if_exists('supabase-dbdev'); +drop extension if exists "supabase-dbdev"; +select + pgtle.install_extension( + 'supabase-dbdev', + resp.contents ->> 'version', + 'PostgreSQL package manager', + resp.contents ->> 'sql' + ) +from http( + ( + 'GET', + 'https://api.database.dev/rest/v1/' + || 'package_versions?select=sql,version' + || '&package_name=eq.supabase-dbdev' + || '&order=version.desc' + || '&limit=1', + array[ + ( + 'apiKey', + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJp' + || 'c3MiOiJzdXBhYmFzZSIsInJlZiI6InhtdXB0cHBsZnZpaWZyY' + || 'ndtbXR2Iiwicm9sZSI6ImFub24iLCJpYXQiOjE2ODAxMDczNzI' + || 'sImV4cCI6MTk5NTY4MzM3Mn0.z2CN0mvO2No8wSi46Gw59DFGCTJ' + || 'rzM0AQKsu_5k134s' + )::http_header + ], + null, + null + ) +) x, +lateral ( + select + ((row_to_json(x) -> 'content') #>> '{}')::json -> 0 +) resp(contents); +create extension "supabase-dbdev"; +select dbdev.install('supabase-dbdev'); +drop extension if exists "supabase-dbdev"; +create extension "supabase-dbdev"; + +-- install test helpers +SELECT dbdev.install('basejump-supabase_test_helpers'); diff --git a/scripts/setup.sh b/scripts/setup.sh new file mode 100755 index 0000000..ae59528 --- /dev/null +++ b/scripts/setup.sh @@ -0,0 +1,9 @@ +# install test helpers (remove once dbdev supports installing remote extensions) +psql -v ON_ERROR_STOP=1 -U postgres -d postgres -h localhost -p 54322 -f ./scripts/install-dbdev-with-test-helpers.sql +# install basejump_core +~/.cargo/bin/dbdev install --connection postgres://postgres:postgres@localhost:54322/postgres --path . +# create basejump_core extension +psql -v ON_ERROR_STOP=1 -U postgres -d postgres -h localhost -p 54322 -c 'CREATE EXTENSION IF NOT EXISTS basejump_core with schema extensions;' + + + diff --git a/scripts/sync-stripe.ts b/scripts/sync-stripe.ts deleted file mode 100644 index 2811954..0000000 --- a/scripts/sync-stripe.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { stripe } from "@/utils/admin/stripe"; -import { - upsertPriceRecord, - upsertProductRecord, -} from "@/utils/admin/stripe-billing-helpers"; - -// check if stripe key is set, exit if not -if (!process.env.STRIPE_SECRET_KEY) { - console.log("No Stripe key found, skipping sync"); - process.exit(0); -} - -// first we pull all products from Stripe and insert them into the billing_products table -stripe.products - .list() - .then(async (products) => { - for (const product of products.data) { - await upsertProductRecord(product); - } - }) - .catch((e) => { - console.log(e); - }); - -// then we pull all prices from Stripe and insert them into the billing_prices table -stripe.prices - .list() - .then(async (prices) => { - for (const price of prices.data) { - await upsertPriceRecord(price); - } - }) - .catch((e) => { - console.log(e); - }); diff --git a/src/components/basejump-default-content/future-content-placeholder.tsx b/src/components/basejump-default-content/future-content-placeholder.tsx deleted file mode 100644 index 1f70daa..0000000 --- a/src/components/basejump-default-content/future-content-placeholder.tsx +++ /dev/null @@ -1,33 +0,0 @@ -/* istanbul ignore file */ -import Image from "next/image"; - -type Props = { - filePath: string; - title?: string; -}; -const FutureContentPlaceholder = ({ - title = "Here's where you bring the awesome!", - filePath, -}: Props) => { - return ( -
- Basejump -

{title}

- {!!filePath && ( -
-

To edit this page, check it out at:

-
-            {filePath}
-          
-
- )} -
- ); -}; - -export default FutureContentPlaceholder; diff --git a/src/components/basejump-default-content/homepage.tsx b/src/components/basejump-default-content/homepage.tsx deleted file mode 100644 index 3f4ee0e..0000000 --- a/src/components/basejump-default-content/homepage.tsx +++ /dev/null @@ -1,231 +0,0 @@ -/* istanbul ignore file */ -import Logo from "@/components/basejump-default-content/logo"; -import ContentMeta from "@/components/content-pages/content-meta"; - -const BasejumpHomepage = () => ( -
- -
- -

- An opinionated shortcut for launching{" "} - Supabase apps with{" "} - NextJS -

-
-
-
-

Features

-
-
-

Teams & Accounts

-

- Billing and permissions for both personal and team accounts. Both - are optional, allowing you to fine-tune your experience -

-
-
-

Stripe Subscriptions

-

- Supports complicated workflows such as trial periods, free plans, - forced payment plans and more -

-
-
-

Easy Permissions

-

- Support for members and owners, easy database functions for - extending permissions as needed -

-
-
-

Account Management & Dashboard

-

- Profiles, billing, team invitations and more. All the basics so - you can focus on your app -

-
-
-

Full Component Library

-

- Basejump leverages DaisyUI and TailwindCSS for an easy development - experience -

-
-
-

Internationalization (I18n)

-

- Support for translations and internationalization using - next-translate -

-
-
-

Authentication

-

- Magic links, Phone SMS Auth, Email/Password and Social Logins all - provided by Supabase -

-
-
-

Fully Customizable

-

- Basejump is a batteries-included starting point for your - application. Customize it to your hearts content -

-
-
-

Themeable

-

- Light & Dark mode included. Create your own themes using DaisyUI -

-
-
-

Open Source

-

- Basejump is open source, contributions are both awesome and - encouraged! -

-
-
-
-
-

- Sensible Defaults & Special Thanks -

-

- Basejump is built using some really awesome open source libraries and - graphics. Here are some of them -

-

Libraries and tools

- -

Graphics

- -
-
-
-); - -export default BasejumpHomepage; diff --git a/src/components/basejump-default-content/logo.tsx b/src/components/basejump-default-content/logo.tsx deleted file mode 100644 index a3170ea..0000000 --- a/src/components/basejump-default-content/logo.tsx +++ /dev/null @@ -1,49 +0,0 @@ -/* istanbul ignore file */ -import Image from "next/image"; -import cx from "classnames"; - -type Props = { - size: "sm" | "lg"; - className?: string; -}; - -const Logo = ({ size = "lg", className }: Props) => { - const height = size === "sm" ? 40 : 150; - const width = size === "sm" ? 40 : 150; - return ( -
-
- Basejump Logo -
-

- Basejump -

-
- ); -}; - -export default Logo; diff --git a/src/components/content-pages/content-footer.tsx b/src/components/content-pages/content-footer.tsx deleted file mode 100644 index 661e1e4..0000000 --- a/src/components/content-pages/content-footer.tsx +++ /dev/null @@ -1,13 +0,0 @@ -const ContentFooter = () => { - const year = new Date().getFullYear(); - return ( -
-
-

Your footer - the place you put footery things

-

-

© {year} usebasejump.com

-
-
- ); -}; -export default ContentFooter; diff --git a/src/components/content-pages/content-header-mobile.tsx b/src/components/content-pages/content-header-mobile.tsx deleted file mode 100644 index b36c65f..0000000 --- a/src/components/content-pages/content-header-mobile.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { Button, Divider, Menu } from "react-daisyui"; -import cx from "classnames"; -import { XIcon } from "@heroicons/react/outline"; -import Logo from "@/components/basejump-default-content/logo"; -import Link from "next/link"; -import useHeaderNavigation from "@/utils/content/use-header-navigation"; -import { useUser } from "@supabase/auth-helpers-react"; -import useTranslation from "next-translate/useTranslation"; - -type Props = { - className?: string; - onClose?: () => void; -}; -const ContentHeaderMobile = ({ className, onClose }: Props) => { - const navigation = useHeaderNavigation(); - const user = useUser(); - const { t } = useTranslation("content"); - return ( -
-
-
- - - - -
- - {navigation.map((item) => ( - - - {item.title} - - - ))} - - {!!user ? ( - - - {t("dashboard")} - - - ) : ( - <> - - - {t("login")} - - - - - {t("signUp")} - - - - )} - -
-
- ); -}; - -export default ContentHeaderMobile; diff --git a/src/components/content-pages/content-header.tsx b/src/components/content-pages/content-header.tsx deleted file mode 100644 index d22ab70..0000000 --- a/src/components/content-pages/content-header.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { Button, Navbar } from "react-daisyui"; -import Link from "next/link"; -import { useUser } from "@supabase/auth-helpers-react"; -import { useRouter } from "next/router"; -import Logo from "@/components/basejump-default-content/logo"; -import useTranslation from "next-translate/useTranslation"; -import { MenuIcon } from "@heroicons/react/outline"; -import useHeaderNavigation from "@/utils/content/use-header-navigation"; - -type Props = { - toggleSidebar: () => void; -}; - -const ContentHeader = ({ toggleSidebar }: Props) => { - const user = useUser(); - const router = useRouter(); - - const { t } = useTranslation("content"); - - const navigation = useHeaderNavigation(); - - return ( - -
- {router.asPath !== "/" && ( - - - - )} -
- {navigation.map((nav) => ( - - {nav.title} - - ))} -
-
-
- {!!user ? ( - - {t("dashboard")} - - ) : ( - <> - - {t("login")} - - - {t("signUp")} - - - )} -
-
- -
-
- ); -}; - -export default ContentHeader; diff --git a/src/components/content-pages/content-layout.tsx b/src/components/content-pages/content-layout.tsx deleted file mode 100644 index 03e4a54..0000000 --- a/src/components/content-pages/content-layout.tsx +++ /dev/null @@ -1,29 +0,0 @@ -import ContentHeader from "./content-header"; -import ContentFooter from "./content-footer"; -import { Drawer } from "react-daisyui"; -import { useToggle } from "react-use"; -import ContentHeaderMobile from "@/components/content-pages/content-header-mobile"; -import { useRouter } from "next/router"; -import { useEffect } from "react"; - -const ContentLayout = ({ children }) => { - const [isSidebarOpen, toggleSidebar] = useToggle(false); - const router = useRouter(); - - useEffect(() => { - toggleSidebar(false); - }, [router.asPath, toggleSidebar]); - return ( - } - open={isSidebarOpen} - onClickOverlay={toggleSidebar} - > - -
{children}
- -
- ); -}; - -export default ContentLayout; diff --git a/src/components/content-pages/content-meta.tsx b/src/components/content-pages/content-meta.tsx deleted file mode 100644 index 59ff7b5..0000000 --- a/src/components/content-pages/content-meta.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import Head from "next/head"; -import getFullRedirectUrl from "@/utils/get-full-redirect-url"; - -type Props = { - title: string; - description: string; - socialDescription?: string; - socialImage?: string; -}; - -const ContentMeta = ({ - title, - description, - socialDescription, - socialImage, -}: Props) => { - return ( - - {title} - - - - - - - {!!socialImage && ( - <> - - - - )} - - ); -}; - -export default ContentMeta; diff --git a/src/components/core/input.tsx b/src/components/core/input.tsx deleted file mode 100644 index 0006069..0000000 --- a/src/components/core/input.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { Input as InnerInput, InputProps } from "react-daisyui"; -import { ForwardedRef, forwardRef } from "react"; -import cx from "classnames"; - -type Props = InputProps & { - label?: string; - helpText?: string; - errorMessage?: string; -}; -const Input = forwardRef( - ( - { label, helpText, errorMessage, color, ...props }: Props, - ref: ForwardedRef - ) => { - return ( -
- - - {(!!helpText || !!errorMessage) && ( - - )} -
- ); - } -); - -Input.displayName = "Input"; - -export default Input; diff --git a/src/components/core/loader.tsx b/src/components/core/loader.tsx deleted file mode 100644 index a48754b..0000000 --- a/src/components/core/loader.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import React, { ReactElement, SVGProps } from "react"; - -export default function Loader( - props: JSX.IntrinsicAttributes & SVGProps -): ReactElement { - const speed = Number(String(props.speed ?? 0.7)); - const fill = props.fill ?? "#000"; - const stroke = props.stroke ?? "transparent"; - const fillOpacity = props.fillOpacity; - const strokeOpacity = props.strokeOpacity; - return ( - - - - - - - - - - - - - - - - - ); -} diff --git a/src/components/core/portal.tsx b/src/components/core/portal.tsx deleted file mode 100644 index ff6b3ed..0000000 --- a/src/components/core/portal.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { ReactNode, useEffect, useState } from "react"; -import { createPortal } from "react-dom"; - -export default function Portal(props: { children: ReactNode }) { - let { children } = props; - let [mounted, setMounted] = useState(false); - - useEffect(() => setMounted(true), []); - - if (!mounted) return null; - return createPortal(children, document.body); -} diff --git a/src/components/core/select.tsx b/src/components/core/select.tsx deleted file mode 100644 index 3688b18..0000000 --- a/src/components/core/select.tsx +++ /dev/null @@ -1,72 +0,0 @@ -import { SelectProps } from "react-daisyui"; -import { ForwardedRef, forwardRef, SelectHTMLAttributes } from "react"; -import { twMerge } from "tailwind-merge"; -import cx from "classnames"; - -type Props = SelectHTMLAttributes & { - label?: string; - helpText?: string; - errorMessage?: string; - color?: SelectProps["color"]; - size?: SelectProps["size"]; - bordered?: boolean; -}; -const Select = forwardRef( - ( - { - label, - helpText, - errorMessage, - color, - value, - children, - className, - bordered = true, - size, - ...props - }: Props, - ref: ForwardedRef - ) => { - return ( -
- - - {(!!helpText || !!errorMessage) && ( - - )} -
- ); - } -); - -Select.displayName = "Select"; - -export default Select; diff --git a/src/components/dashboard/accounts/account-subscription-takeover/account-subscription-takeover.tsx b/src/components/dashboard/accounts/account-subscription-takeover/account-subscription-takeover.tsx deleted file mode 100644 index 153a4d8..0000000 --- a/src/components/dashboard/accounts/account-subscription-takeover/account-subscription-takeover.tsx +++ /dev/null @@ -1,46 +0,0 @@ -import { UseDashboardOverviewResponse } from "@/utils/api/use-dashboard-overview"; -import useAccountBillingStatus from "@/utils/api/use-account-billing-status"; -import NewSubscription from "@/components/dashboard/accounts/account-subscription-takeover/new-subscription"; -import { MANUAL_SUBSCRIPTION_REQUIRED } from "@/types/billing"; -import useTranslation from "next-translate/useTranslation"; -import AccountSubscription from "@/components/dashboard/accounts/settings/account-subscription"; - -type Props = { - currentAccount: UseDashboardOverviewResponse[0]; -}; -const AccountSubscriptionTakeover = ({ currentAccount }: Props) => { - const { t } = useTranslation("dashboard"); - const { data: subscriptionData } = useAccountBillingStatus( - currentAccount?.account_id - ); - return ( -
- {[ - "incomplete_expired", - "canceled", - MANUAL_SUBSCRIPTION_REQUIRED, - ].includes(subscriptionData?.status) && ( - <> -

- {t("accountSubscriptionTakeover.newSubscriptionTitle")} -

- - - )} - {["incomplete", "past_due", "unpaid"].includes( - subscriptionData?.status - ) && ( - <> -

- {t( - `accountSubscriptionTakeover.fixExistingSubscriptionTitle.${subscriptionData.status}` - )} -

- - - )} -
- ); -}; - -export default AccountSubscriptionTakeover; diff --git a/src/components/dashboard/accounts/account-subscription-takeover/individual-subscription-plan.tsx b/src/components/dashboard/accounts/account-subscription-takeover/individual-subscription-plan.tsx deleted file mode 100644 index 55998fd..0000000 --- a/src/components/dashboard/accounts/account-subscription-takeover/individual-subscription-plan.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { Button } from "react-daisyui"; -import { useRouter } from "next/router"; -import { UseAccountBillingOptionsResponse } from "@/utils/api/use-account-billing-options"; -import useTranslation from "next-translate/useTranslation"; -import { useMutation } from "@tanstack/react-query"; -import { UseDashboardOverviewResponse } from "@/utils/api/use-dashboard-overview"; -import { toast } from "react-toastify"; - -type Props = { - plan: UseAccountBillingOptionsResponse[0]; - currentAccount: UseDashboardOverviewResponse[0]; -}; -const IndividualSubscriptionPlan = ({ plan, currentAccount }: Props) => { - const router = useRouter(); - const { t } = useTranslation("dashboard"); - - const setupCheckoutLink = useMutation( - async () => { - const res = await fetch("/api/billing/setup", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ - accountId: currentAccount?.account_id, - priceId: plan.price_id, - }), - }); - - const jsonResponse = await res.json(); - - if (!res.ok) { - throw new Error(jsonResponse.error); - } - return jsonResponse.url; - }, - { - onSuccess(url) { - console.log("whoooop", url); - if (!url) return; - window.location.href = url; - }, - onError(error: any) { - toast.error(error.message); - }, - } - ); - - return ( -
-
-

{plan.product_name}

-

{plan.product_description}

-
-
-
-

- {new Intl.NumberFormat(router.locale, { - style: "currency", - currency: plan.currency, - minimumFractionDigits: 0, - }).format((plan.price || 0) / 100)} -

-

/ {t(`newSubscriptions.intervals.${plan.interval}`)}

-
- -
-
- ); -}; - -export default IndividualSubscriptionPlan; diff --git a/src/components/dashboard/accounts/account-subscription-takeover/new-subscription.tsx b/src/components/dashboard/accounts/account-subscription-takeover/new-subscription.tsx deleted file mode 100644 index cd6ffe5..0000000 --- a/src/components/dashboard/accounts/account-subscription-takeover/new-subscription.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { useMemo, useState } from "react"; -import useAccountBillingOptions from "@/utils/api/use-account-billing-options"; -import { Button, ButtonGroup } from "react-daisyui"; -import useTranslation from "next-translate/useTranslation"; -import IndividualSubscriptionPlan from "@/components/dashboard/accounts/account-subscription-takeover/individual-subscription-plan"; - -const NewSubscription = ({ currentAccount }) => { - const [activeTab, setActiveTab] = useState(null); - const [tabs, setTabs] = useState([]); - const { t } = useTranslation("dashboard"); - - const { data } = useAccountBillingOptions(currentAccount?.account_id, { - onSuccess(data) { - const options = new Set(data?.map((option) => option.interval)); - setTabs(Array.from(options)); - if (!activeTab || !options.has(activeTab)) { - setActiveTab(Array.from(options)[0]); - } - }, - }); - - const currentOptions = useMemo(() => { - return data?.filter((option) => option.interval === activeTab) || []; - }, [data, activeTab]); - - return ( -
- - {tabs.map((tab) => ( - - ))} - -
- {currentOptions.map((option) => ( - - ))} -
-
- ); -}; - -export default NewSubscription; diff --git a/src/components/dashboard/accounts/new-account-form.tsx b/src/components/dashboard/accounts/new-account-form.tsx deleted file mode 100644 index c64ac91..0000000 --- a/src/components/dashboard/accounts/new-account-form.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { useForm } from "react-hook-form"; -import { toast } from "react-toastify"; -import { useSupabaseClient, useUser } from "@supabase/auth-helpers-react"; -import Input from "@/components/core/input"; -import { Button } from "react-daisyui"; -import useTranslation from "next-translate/useTranslation"; -import { Database } from "@/types/supabase-types"; - -type Props = { - onComplete: (accountId: string) => void; -}; - -type FORM_DATA = { - name: string; -}; - -const NewAccountForm = ({ onComplete }: Props) => { - const user = useUser(); - const supabaseClient = useSupabaseClient(); - const { t } = useTranslation("dashboard"); - const { - register, - handleSubmit, - reset, - formState: { isSubmitting, errors }, - } = useForm(); - - async function onSubmit(data: FORM_DATA) { - if (!user) return; - const response = await supabaseClient - .from("accounts") - .insert({ - team_name: data.name, - }) - .select(); - - if (response.error) { - toast.error(response.error.message); - } - - if (response?.data?.[0]?.id) { - reset(); - toast.success(t("shared.successfulChange")); - if (onComplete) { - onComplete(response.data?.[0]?.id); - } - } - } - - return ( -
- - -
- ); -}; - -export default NewAccountForm; diff --git a/src/components/dashboard/accounts/new-account-modal.tsx b/src/components/dashboard/accounts/new-account-modal.tsx deleted file mode 100644 index 7527f48..0000000 --- a/src/components/dashboard/accounts/new-account-modal.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { Modal } from "react-daisyui"; -import NewAccountForm from "@/components/dashboard/accounts/new-account-form"; -import useTranslation from "next-translate/useTranslation"; -import Portal from "@/components/core/portal"; - -type Props = { - open: boolean; - onComplete: (accountId: string) => void; - onClose: () => void; -}; -const NewAccountModal = ({ open, onClose, onComplete }: Props) => { - const { t } = useTranslation("dashboard"); - return ( - - - - {t("newAccountModal.title")} - - - - - - - ); -}; - -export default NewAccountModal; diff --git a/src/components/dashboard/accounts/personal-account-deactivated.tsx b/src/components/dashboard/accounts/personal-account-deactivated.tsx deleted file mode 100644 index a5f60e1..0000000 --- a/src/components/dashboard/accounts/personal-account-deactivated.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import ListTeams from "@/components/dashboard/profile/list-teams"; -import useTeamAccounts from "@/utils/api/use-team-accounts"; -import NewAccountForm from "@/components/dashboard/accounts/new-account-form"; -import Loader from "@/components/core/loader"; -import { useRouter } from "next/router"; -import useTranslation from "next-translate/useTranslation"; - -const PersonalAccountDeactivated = () => { - const { data, isLoading } = useTeamAccounts(); - const router = useRouter(); - const { t } = useTranslation("dashboard"); - return ( -
- {isLoading ? ( - - ) : !data || data?.length === 0 ? ( -
-

- {t("personalAccountDeactivated.createFirstAccount")} -

-

- {t("personalAccountDeactivated.firstAccountDescription")} -

- - router.push(`/dashboard/teams/${accountId}`) - } - /> -
- ) : ( - - )} -
- ); -}; - -export default PersonalAccountDeactivated; diff --git a/src/components/dashboard/accounts/settings/account-settings-layout.tsx b/src/components/dashboard/accounts/settings/account-settings-layout.tsx deleted file mode 100644 index 3c8d426..0000000 --- a/src/components/dashboard/accounts/settings/account-settings-layout.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import DashboardContent from "@/components/dashboard/shared/dashboard-content"; -import useTranslation from "next-translate/useTranslation"; -import { useRouter } from "next/router"; -import { useMemo } from "react"; -import Link from "next/link"; -import cx from "classnames"; - -const AccountSettingsLayout = ({ children }) => { - const { t } = useTranslation("dashboard"); - const router = useRouter(); - const { accountId } = router.query; - const tabs = useMemo(() => { - return [ - { - label: t("teamAccountSettings.tabs.general"), - href: `/dashboard/teams/${accountId}/settings`, - isActive: router.asPath === `/dashboard/teams/${accountId}/settings`, - }, - { - label: t("teamAccountSettings.tabs.members"), - href: `/dashboard/teams/${accountId}/settings/members`, - isActive: - router.asPath === `/dashboard/teams/${accountId}/settings/members`, - }, - { - label: t("teamAccountSettings.tabs.billing"), - href: `/dashboard/teams/${accountId}/settings/billing`, - isActive: - router.asPath === `/dashboard/teams/${accountId}/settings/billing`, - }, - ]; - }, [accountId, router.asPath, t]); - return ( - - - {t("teamAccountSettings.pageTitle")} - - -
- {tabs.map(({ label, href, isActive }) => ( - - {label} - - ))} -
-
- {children} -
- ); -}; - -export default AccountSettingsLayout; diff --git a/src/components/dashboard/accounts/settings/account-subscription.tsx b/src/components/dashboard/accounts/settings/account-subscription.tsx deleted file mode 100644 index e9b0746..0000000 --- a/src/components/dashboard/accounts/settings/account-subscription.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import SettingsCard from "@/components/dashboard/shared/settings-card"; -import useTranslation from "next-translate/useTranslation"; -import useAccountBillingStatus from "@/utils/api/use-account-billing-status"; -import { Button } from "react-daisyui"; -import { useMutation } from "@tanstack/react-query"; - -type Props = { - accountId: string; -}; - -const AccountSubscription = ({ accountId }: Props) => { - const { t } = useTranslation("dashboard"); - - const { data } = useAccountBillingStatus(accountId); - - const getSubscriptionUrl = useMutation( - async () => { - const res = await fetch("/api/billing/portal-link", { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify({ accountId }), - }); - const { url } = await res.json(); - return url; - }, - { - onSuccess(url) { - window.location.href = url; - }, - } - ); - return ( - <> - {data?.billing_enabled === false ? ( -
-

- {t("accountSubscription.billingDisabled")} -

-

- {t("accountSubscription.billingDisabledDescription")} -

-
- ) : ( - - -

- {data?.plan_name} - {data?.status} -

-

- {t("accountSubscription.billingEmail", { - email: data?.billing_email, - })} -

-
- - - -
- )} - - ); -}; - -export default AccountSubscription; diff --git a/src/components/dashboard/accounts/settings/individual-team-invitation.tsx b/src/components/dashboard/accounts/settings/individual-team-invitation.tsx deleted file mode 100644 index 752a99f..0000000 --- a/src/components/dashboard/accounts/settings/individual-team-invitation.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import formatDistance from "date-fns/formatDistance"; -import { Badge, Button } from "react-daisyui"; -import { ClipboardCopyIcon, TrashIcon } from "@heroicons/react/outline"; -import useTranslation from "next-translate/useTranslation"; -import { useCopyToClipboard } from "react-use"; -import getInvitationUrl from "@/utils/get-invitation-url"; -import { useMutation } from "@tanstack/react-query"; -import { toast } from "react-toastify"; -import { useSupabaseClient } from "@supabase/auth-helpers-react"; -import { Database } from "@/types/supabase-types"; - -type Props = { - invitation: Database["public"]["Tables"]["invitations"]["Row"]; - onChange?: () => void; -}; -const IndividualTeamInvitation = ({ invitation, onChange }: Props) => { - const { t } = useTranslation("dashboard"); - const supabaseClient = useSupabaseClient(); - const [state, copyToClipboard] = useCopyToClipboard(); - - const deleteInvitation = useMutation( - async (invitationId: string) => { - const { data, error } = await supabaseClient - .from("invitations") - .delete() - .match({ id: invitationId }); - if (error) { - toast.error(error.message); - } - - return { data, error }; - }, - { - onSuccess() { - onChange?.(); - }, - } - ); - - return ( -
-
-

- {t("listTeamInvitations.linkDescription", { - date: formatDistance(new Date(invitation.created_at), new Date(), { - addSuffix: true, - }), - })} -

-
- - {invitation.invitation_type} - - {invitation.account_role} -
-
-
- - -
-
- ); -}; - -export default IndividualTeamInvitation; diff --git a/src/components/dashboard/accounts/settings/individual-team-member.tsx b/src/components/dashboard/accounts/settings/individual-team-member.tsx deleted file mode 100644 index 125b286..0000000 --- a/src/components/dashboard/accounts/settings/individual-team-member.tsx +++ /dev/null @@ -1,71 +0,0 @@ -import { Badge, Button, Dropdown, Modal } from "react-daisyui"; -import { DotsHorizontalIcon } from "@heroicons/react/outline"; -import useTranslation from "next-translate/useTranslation"; -import Portal from "@/components/core/portal"; -import UpdateTeamMemberRole from "@/components/dashboard/accounts/settings/update-team-member-role"; -import { useToggle } from "react-use"; -import RemoveTeamMember from "@/components/dashboard/accounts/settings/remove-team-member"; - -const IndividualTeamMember = ({ member }) => { - const [updateRole, toggleUpdateRole] = useToggle(false); - const [removeMember, toggleRemoveMember] = useToggle(false); - const { t } = useTranslation("dashboard"); - - return ( - <> -
-
-

{member.name}

-
- {member.is_primary_owner ? ( - {t("listTeamMembers.primaryOwner")} - ) : ( - {member.account_role} - )} -
-
- {!member.is_primary_owner && ( -
- - - - toggleUpdateRole(true)}> - {t("listTeamMembers.updateRole")} - - toggleRemoveMember(true)}> - {t("listTeamMembers.removeMember")} - - - -
- )} -
- - - {updateRole && ( - - - {t("listTeamMembers.updateRoleTitle", { name: member.name })} - - toggleUpdateRole(false)} - /> - - )} - {removeMember && ( - - - {t("listTeamMembers.removeMemberTitle", { name: member.name })} - - - - )} - - - ); -}; - -export default IndividualTeamMember; diff --git a/src/components/dashboard/accounts/settings/invite-member.tsx b/src/components/dashboard/accounts/settings/invite-member.tsx deleted file mode 100644 index 74bb5f0..0000000 --- a/src/components/dashboard/accounts/settings/invite-member.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import { useForm } from "react-hook-form"; -import handleSupabaseErrors from "@/utils/handle-supabase-errors"; -import { Alert, Button } from "react-daisyui"; -import useTranslation from "next-translate/useTranslation"; -import Select from "@/components/core/select"; -import { useState } from "react"; -import getInvitationUrl from "@/utils/get-invitation-url"; -import { useCopyToClipboard } from "react-use"; -import { ClipboardCopyIcon } from "@heroicons/react/outline"; -import { useSupabaseClient, useUser } from "@supabase/auth-helpers-react"; -import { Database } from "@/types/supabase-types"; - -type Props = { - accountId: string; - onComplete?: () => void; -}; - -type INVITE_FORM = { - invitationType: "one-time" | "24-hour"; - email: string; - userType: Database["public"]["Tables"]["invitations"]["Row"]["account_role"]; -}; - -type INVITE_FORM_USER_TYPES = Array<{ - value: Database["public"]["Tables"]["invitations"]["Row"]["account_role"]; - label: string; -}>; - -export const userTypeOptions: INVITE_FORM_USER_TYPES = [ - { - label: "Owner", - value: "owner", - }, - { - label: "Member", - value: "member", - }, -]; - -const invitationTypes = [ - { - label: "One-time link", - value: "one-time", - }, - { - label: "24 hour link", - value: "24-hour", - }, -]; - -const defaultInvitationType = "one-time"; - -const InviteMember = ({ accountId, onComplete }: Props) => { - const [invitationLink, setInvitationLink] = useState(null); - const [state, copyToClipboard] = useCopyToClipboard(); - const { t } = useTranslation("dashboard"); - const user = useUser(); - const supabaseClient = useSupabaseClient(); - const { - register, - handleSubmit, - formState: { isSubmitting }, - } = useForm(); - - async function onSubmit(invitation: INVITE_FORM) { - const { data, error } = await supabaseClient - .from("invitations") - .insert({ - invitation_type: invitation.invitationType, - invited_by_user_id: user.id, - account_id: accountId, - account_role: invitation.userType, - }) - .select(); - - handleSupabaseErrors(data, error); - - if (data) { - setInvitationLink(getInvitationUrl(data?.[0]?.token)); - } - - if (!error && onComplete) { - onComplete(); - } - } - - return ( - <> -
-
- - - -
-
- {!!invitationLink && ( - -
-

{t("inviteMember.linkGenerated")}

- -
-
- )} - - ); -}; - -export default InviteMember; diff --git a/src/components/dashboard/accounts/settings/list-team-invitations.tsx b/src/components/dashboard/accounts/settings/list-team-invitations.tsx deleted file mode 100644 index c412601..0000000 --- a/src/components/dashboard/accounts/settings/list-team-invitations.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import useTranslation from "next-translate/useTranslation"; -import Loader from "@/components/core/loader"; -import useTeamInvitations from "@/utils/api/use-team-invitations"; -import IndividualTeamInvitation from "@/components/dashboard/accounts/settings/individual-team-invitation"; - -type Props = { - accountId: string; -}; -const ListTeamInvitations = ({ accountId }: Props) => { - const { t } = useTranslation("dashboard"); - const { data, isLoading, refetch } = useTeamInvitations(accountId); - - return ( - <> - {isLoading ? ( - - ) : ( -
- {data?.map((invitation) => ( - refetch()} - key={invitation.id} - invitation={invitation} - /> - ))} -
- )} - - ); -}; - -export default ListTeamInvitations; diff --git a/src/components/dashboard/accounts/settings/list-team-members.tsx b/src/components/dashboard/accounts/settings/list-team-members.tsx deleted file mode 100644 index f033540..0000000 --- a/src/components/dashboard/accounts/settings/list-team-members.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import SettingsCard from "@/components/dashboard/shared/settings-card"; -import useTranslation from "next-translate/useTranslation"; -import Loader from "@/components/core/loader"; -import useTeamMembers from "@/utils/api/use-team-members"; -import IndividualTeamMember from "@/components/dashboard/accounts/settings/individual-team-member"; - -type Props = { - accountId: string; -}; -const ListTeamMembers = ({ accountId }: Props) => { - const { t } = useTranslation("dashboard"); - const { data, isLoading } = useTeamMembers(accountId); - - return ( - - {isLoading ? ( - - ) : ( -
- {data?.map((member) => ( - - ))} -
- )} -
- ); -}; - -export default ListTeamMembers; diff --git a/src/components/dashboard/accounts/settings/remove-team-member.tsx b/src/components/dashboard/accounts/settings/remove-team-member.tsx deleted file mode 100644 index 3097d98..0000000 --- a/src/components/dashboard/accounts/settings/remove-team-member.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import { useMutation, useQueryClient } from "@tanstack/react-query"; -import useTranslation from "next-translate/useTranslation"; -import { toast } from "react-toastify"; -import { Button } from "react-daisyui"; -import { UseTeamMembersResponse } from "@/utils/api/use-team-members"; -import { useSupabaseClient } from "@supabase/auth-helpers-react"; -import { Database } from "@/types/supabase-types"; - -type Props = { - member: UseTeamMembersResponse; - onComplete?: () => void; -}; - -const RemoveTeamMember = ({ member, onComplete }: Props) => { - const queryClient = useQueryClient(); - const supabaseClient = useSupabaseClient(); - - const { t } = useTranslation("dashboard"); - - const removeMember = useMutation(async () => { - const { error } = await supabaseClient - .from("account_user") - .delete() - .eq("user_id", member.user_id) - .eq("account_id", member.account_id); - if (error) { - toast.error(error.message); - } - await queryClient.invalidateQueries(["teamMembers"]); - if (onComplete) { - onComplete(); - } - }); - - return ( - - ); -}; - -export default RemoveTeamMember; diff --git a/src/components/dashboard/accounts/settings/update-account-name.tsx b/src/components/dashboard/accounts/settings/update-account-name.tsx deleted file mode 100644 index 165ec8d..0000000 --- a/src/components/dashboard/accounts/settings/update-account-name.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { useSupabaseClient, useUser } from "@supabase/auth-helpers-react"; -import { useForm } from "react-hook-form"; -import SettingsCard from "@/components/dashboard/shared/settings-card"; -import useTranslation from "next-translate/useTranslation"; -import { Button } from "react-daisyui"; -import Input from "@/components/core/input"; -import { toast } from "react-toastify"; -import useTeamAccount from "@/utils/api/use-team-account"; -import { Database } from "@/types/supabase-types"; - -type FORM_DATA = { - name: string; -}; - -type Props = { - accountId: string; -}; - -const UpdateAccountName = ({ accountId }: Props) => { - const user = useUser(); - const supabaseClient = useSupabaseClient(); - const { data } = useTeamAccount(accountId); - const { t } = useTranslation("dashboard"); - const { - register, - handleSubmit, - - formState: { isSubmitting, errors }, - } = useForm(); - - async function onSubmit(data: FORM_DATA) { - if (!user || !accountId) return; - const response = await supabaseClient - .from("accounts") - .update({ - team_name: data.name, - }) - .match({ id: accountId }); - if (response.error) { - toast.error(response.error.message || response.statusText); - } - - if (!!response.data) { - toast.success(t("shared.successfulChange")); - } - } - - return ( - - {!!data && ( -
- - - - - - -
- )} -
- ); -}; - -export default UpdateAccountName; diff --git a/src/components/dashboard/accounts/settings/update-team-member-role.tsx b/src/components/dashboard/accounts/settings/update-team-member-role.tsx deleted file mode 100644 index 02100fd..0000000 --- a/src/components/dashboard/accounts/settings/update-team-member-role.tsx +++ /dev/null @@ -1,90 +0,0 @@ -import { useForm } from "react-hook-form"; -import { Button, Checkbox } from "react-daisyui"; -import useTranslation from "next-translate/useTranslation"; -import Select from "@/components/core/select"; -import { userTypeOptions } from "@/components/dashboard/accounts/settings/invite-member"; -import useTeamRole from "@/utils/api/use-team-role"; -import { UseTeamMembersResponse } from "@/utils/api/use-team-members"; -import { toast } from "react-toastify"; -import { useQueryClient } from "@tanstack/react-query"; -import { useSupabaseClient } from "@supabase/auth-helpers-react"; -import { Database } from "@/types/supabase-types"; - -type USER_ROLE_FORM = { - account_role: Database["public"]["Tables"]["account_user"]["Row"]["account_role"]; - make_primary_owner?: boolean; -}; - -type Props = { - member: UseTeamMembersResponse; - onComplete?: () => void; -}; - -const UpdateTeamMemberRole = ({ member, onComplete }: Props) => { - const { t } = useTranslation("dashboard"); - const supabaseClient = useSupabaseClient(); - const { isPrimaryOwner } = useTeamRole(member.account_id); - const queryClient = useQueryClient(); - const { - register, - handleSubmit, - watch, - formState: { isSubmitting }, - } = useForm({ - defaultValues: { - account_role: member.account_role, - make_primary_owner: false, - }, - }); - - async function onSubmit(formData) { - const { error } = await supabaseClient.rpc("update_account_user_role", { - account_id: member.account_id, - user_id: member.user_id, - new_account_role: formData.account_role, - make_primary_owner: formData.make_primary_owner, - }); - if (error) { - toast.error(error.message); - } - await queryClient.invalidateQueries(["teamMembers"]); - if (onComplete) { - onComplete(); - } - } - - const isOwner = watch("account_role") === "owner"; - - return ( -
- - {isOwner && isPrimaryOwner && ( -
- -
- )} - -
- ); -}; - -export default UpdateTeamMemberRole; diff --git a/src/components/dashboard/authentication/login-magic-link.tsx b/src/components/dashboard/authentication/login-magic-link.tsx deleted file mode 100644 index a2809e9..0000000 --- a/src/components/dashboard/authentication/login-magic-link.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { useState } from "react"; -import { useForm } from "react-hook-form"; -import { Button, Input } from "react-daisyui"; -import useTranslation from "next-translate/useTranslation"; -import getFullRedirectUrl from "@/utils/get-full-redirect-url"; -import { DASHBOARD_PATH } from "@/types/auth"; -import { useSupabaseClient } from "@supabase/auth-helpers-react"; -import { Database } from "@/types/supabase-types"; - -type LOGIN_FORM = { - email: string; -}; - -type Props = { - redirectTo?: string; - buttonText?: string; -}; - -const LoginMagicLink = ({ redirectTo = DASHBOARD_PATH, buttonText }: Props) => { - const { t } = useTranslation("authentication"); - const supabaseClient = useSupabaseClient(); - const [emailSent, setEmailSent] = useState(false); - - const { - register, - handleSubmit, - formState: { isSubmitting }, - } = useForm(); - - async function onSubmit({ email }: LOGIN_FORM) { - const { error } = await supabaseClient.auth.signInWithOtp({ - email, - options: { - shouldCreateUser: true, - emailRedirectTo: getFullRedirectUrl(redirectTo), - }, - }); - if (error) throw error; - setEmailSent(true); - } - - return ( - <> - {emailSent ? ( -
-

{t("magicLink.checkEmail")}

- -
- ) : ( -
-
- - - -
- -
- )} - - ); -}; - -export default LoginMagicLink; diff --git a/src/components/dashboard/authentication/login-password.tsx b/src/components/dashboard/authentication/login-password.tsx deleted file mode 100644 index c078ec6..0000000 --- a/src/components/dashboard/authentication/login-password.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { useForm } from "react-hook-form"; -import { Button } from "react-daisyui"; -import useTranslation from "next-translate/useTranslation"; -import Input from "@/components/core/input"; -import { toast } from "react-toastify"; -import { DASHBOARD_PATH } from "@/types/auth"; -import { useRouter } from "next/router"; -import { useSupabaseClient } from "@supabase/auth-helpers-react"; - -type LOGIN_FORM = { - email: string; - password: string; -}; - -type Props = { - redirectTo?: string; - buttonText?: string; -}; - -const LoginPassword = ({ redirectTo = DASHBOARD_PATH, buttonText }: Props) => { - const { t } = useTranslation("authentication"); - const router = useRouter(); - const supabaseClient = useSupabaseClient(); - const { - register, - handleSubmit, - formState: { isSubmitting }, - } = useForm(); - - async function onSubmit({ email, password }: LOGIN_FORM) { - const { error } = await supabaseClient.auth.signInWithPassword({ - email, - password, - }); - if (error) toast.error(error.message); - if (!error) { - await router.push(redirectTo); - } - } - - return ( -
- - - -
- ); -}; - -export default LoginPassword; diff --git a/src/components/dashboard/authentication/signup-password.tsx b/src/components/dashboard/authentication/signup-password.tsx deleted file mode 100644 index 7cdea40..0000000 --- a/src/components/dashboard/authentication/signup-password.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import { useForm } from "react-hook-form"; -import { Button } from "react-daisyui"; -import useTranslation from "next-translate/useTranslation"; -import Input from "@/components/core/input"; -import { toast } from "react-toastify"; -import { DASHBOARD_PATH } from "@/types/auth"; -import { useRouter } from "next/router"; -import getFullRedirectUrl from "@/utils/get-full-redirect-url"; -import { useSupabaseClient } from "@supabase/auth-helpers-react"; - -type LOGIN_FORM = { - email: string; - password: string; -}; - -type Props = { - redirectTo?: string; - buttonText?: string; - metadata?: { - [key: string]: string | number | boolean; - }; -}; - -const SignupPassword = ({ - redirectTo = DASHBOARD_PATH, - buttonText, - metadata, -}: Props) => { - const { t } = useTranslation("authentication"); - const router = useRouter(); - const supabaseClient = useSupabaseClient(); - const { - register, - handleSubmit, - formState: { isSubmitting }, - } = useForm(); - - async function onSubmit({ email, password }: LOGIN_FORM) { - const { error } = await supabaseClient.auth.signUp({ - email, - password, - options: { - data: { - ...metadata, - }, - emailRedirectTo: getFullRedirectUrl(redirectTo), - }, - }); - if (error) toast.error(error.message); - if (!error) { - await router.push(redirectTo); - } - } - - return ( -
- - - -
- ); -}; - -export default SignupPassword; diff --git a/src/components/dashboard/dashboard-layout.tsx b/src/components/dashboard/dashboard-layout.tsx deleted file mode 100644 index 7416090..0000000 --- a/src/components/dashboard/dashboard-layout.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { useEffect, useMemo, useState } from "react"; -import { Button, Drawer } from "react-daisyui"; -import SidebarMenu from "./sidebar/sidebar-menu"; -import useDashboardOverview from "@/utils/api/use-dashboard-overview"; -import { useRouter } from "next/router"; -import AccountSubscriptionTakeover from "@/components/dashboard/accounts/account-subscription-takeover/account-subscription-takeover"; -import useAccountBillingStatus from "@/utils/api/use-account-billing-status"; -import { MenuIcon } from "@heroicons/react/outline"; -import Logo from "@/components/basejump-default-content/logo"; - -const DashboardLayout = ({ children }) => { - const [isSidebarOpen, setIsSidebarOpen] = useState(false); - const router = useRouter(); - const { accountId } = router.query; - - function toggleSidebar() { - setIsSidebarOpen(!isSidebarOpen); - } - - const { data, refetch: refetchDashboardOverview } = useDashboardOverview(); - - useEffect(() => { - /** - * Close sidebar when route changes - */ - setIsSidebarOpen(false); - refetchDashboardOverview(); - }, [router.asPath, refetchDashboardOverview]); - const currentAccount = useMemo(() => { - if (!accountId) { - return data?.find((a) => a.personal_account); - } - return data?.find((a) => a.account_id === accountId); - }, [data, accountId]); - - const { data: subscriptionData } = useAccountBillingStatus( - currentAccount?.account_id - ); - - return ( -
- - } - mobile - open={isSidebarOpen} - onClickOverlay={toggleSidebar} - > -
-
- - -
- {children} -
-
- {subscriptionData?.billing_enabled && - !["active", "trialing"].includes(subscriptionData?.status) && ( - - )} -
- ); -}; - -export default DashboardLayout; diff --git a/src/components/dashboard/dashboard-meta.tsx b/src/components/dashboard/dashboard-meta.tsx deleted file mode 100644 index 9f317e1..0000000 --- a/src/components/dashboard/dashboard-meta.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import Head from "next/head"; - -type Props = { - title: string; -}; - -const DashboardMeta = ({ title }: Props) => { - return ( - - {title} - - ); -}; -export default DashboardMeta; diff --git a/src/components/dashboard/profile/list-teams.tsx b/src/components/dashboard/profile/list-teams.tsx deleted file mode 100644 index 643eaa4..0000000 --- a/src/components/dashboard/profile/list-teams.tsx +++ /dev/null @@ -1,75 +0,0 @@ -import useTeamAccounts from "@/utils/api/use-team-accounts"; -import SettingsCard from "@/components/dashboard/shared/settings-card"; -import useTranslation from "next-translate/useTranslation"; -import Loader from "@/components/core/loader"; -import { DASHBOARD_PATH } from "@/types/auth"; -import { useRouter } from "next/router"; -import { useToggle } from "react-use"; -import NewAccountModal from "@/components/dashboard/accounts/new-account-modal"; -import { Button } from "react-daisyui"; -import Link from "next/link"; - -const ListTeams = () => { - const [newAccount, toggleNewAccount] = useToggle(false); - const { t } = useTranslation("dashboard"); - const router = useRouter(); - const { data, isLoading, refetch } = useTeamAccounts(); - - async function onAccountCreated(accountId: string) { - await refetch(); - toggleNewAccount(false); - await router.push(`${DASHBOARD_PATH}?accountId=${accountId}`); - } - - return ( - <> - - {isLoading ? ( - - ) : ( -
- {data?.map((account) => ( -
-
-

{account.team_name}

-

{account.account_role}

-
-
- - {t("listTeams.viewTeam")} - - - {t("listTeams.manageTeam")} - -
-
- ))} -
- )} - - - -
- - - ); -}; - -export default ListTeams; diff --git a/src/components/dashboard/profile/update-email-address.tsx b/src/components/dashboard/profile/update-email-address.tsx deleted file mode 100644 index 6413f22..0000000 --- a/src/components/dashboard/profile/update-email-address.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import { useSupabaseClient, useUser } from "@supabase/auth-helpers-react"; -import { useForm } from "react-hook-form"; -import SettingsCard from "@/components/dashboard/shared/settings-card"; -import useTranslation from "next-translate/useTranslation"; -import { Button } from "react-daisyui"; -import Input from "@/components/core/input"; -import { toast } from "react-toastify"; -import { Database } from "@/types/supabase-types"; - -type FORM_DATA = { - email: string; -}; - -const UpdateEmailAddress = () => { - const user = useUser(); - const supabaseClient = useSupabaseClient(); - const { t } = useTranslation("dashboard"); - const { - register, - handleSubmit, - formState: { isSubmitting, errors }, - } = useForm(); - - async function onSubmit(newEmail: FORM_DATA) { - if (!user) return; - const { data, error } = await supabaseClient.auth.updateUser({ - email: newEmail.email, - }); - if (error) { - toast.error(error.message); - return; - } - if (!!data?.user?.new_email) { - toast.success(t("updateEmailAddress.successfulChange")); - } - } - - return ( - - {!!user && ( -
- - - - - - -
- )} -
- ); -}; - -export default UpdateEmailAddress; diff --git a/src/components/dashboard/profile/update-profile-name.tsx b/src/components/dashboard/profile/update-profile-name.tsx deleted file mode 100644 index f54b9d9..0000000 --- a/src/components/dashboard/profile/update-profile-name.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { useSupabaseClient, useUser } from "@supabase/auth-helpers-react"; -import { useForm } from "react-hook-form"; -import useUserProfile from "@/utils/api/use-user-profile"; -import SettingsCard from "@/components/dashboard/shared/settings-card"; -import useTranslation from "next-translate/useTranslation"; -import { Button } from "react-daisyui"; -import Input from "@/components/core/input"; -import { toast } from "react-toastify"; -import { Database } from "@/types/supabase-types"; - -type FORM_DATA = { - name: string; -}; - -const UpdateProfileName = () => { - const user = useUser(); - const supabaseClient = useSupabaseClient(); - const { data: profile } = useUserProfile(); - const { t } = useTranslation("dashboard"); - const { - register, - handleSubmit, - formState: { isSubmitting, errors }, - } = useForm(); - - async function onSubmit(data: FORM_DATA) { - if (!user) return; - const response = await supabaseClient - .from("profiles") - .update({ - name: data.name, - }) - .eq("id", user.id); - - if (response.error) { - toast.error(response.error.message); - } - - if (!!data) { - toast.success(t("shared.successfulChange")); - } - } - - return ( - - {!!profile && ( -
- - - - - - -
- )} -
- ); -}; - -export default UpdateProfileName; diff --git a/src/components/dashboard/shared/dashboard-content.tsx b/src/components/dashboard/shared/dashboard-content.tsx deleted file mode 100644 index 21156e3..0000000 --- a/src/components/dashboard/shared/dashboard-content.tsx +++ /dev/null @@ -1,49 +0,0 @@ -/** - * Only used for formatting downstream, makes it clear that the - * children are the content of the dashboard content - * @param children - * @constructor - */ -const DashboardContent = ({ children }) => { - return
{children}
; -}; - -/** - * Sets up the title for the dashboard content - * @param children - * @constructor - */ -function Title({ children }) { - return ( -
- {children} -
- ); -} - -/** - * Sets up the container for the dashboard content - * Handles max-width and top border primarily - * @param children - * @constructor - */ -function Content({ children }) { - return ( -
-
{children}
-
- ); -} - -function Tabs({ children }) { - return ( -
-
{children}
-
- ); -} - -DashboardContent.Tabs = Tabs; -DashboardContent.Title = Title; -DashboardContent.Content = Content; -export default DashboardContent; diff --git a/src/components/dashboard/shared/settings-card.tsx b/src/components/dashboard/shared/settings-card.tsx deleted file mode 100644 index 997ae44..0000000 --- a/src/components/dashboard/shared/settings-card.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { ReactNode } from "react"; -import cx from "classnames"; - -type Props = { - className?: string; - children: ReactNode; - title?: string; - description?: string; - disabled?: boolean; -}; -const SettingsCard = ({ - children, - title, - description, - disabled, - className, -}: Props) => ( -
- {!!title && ( -
-

{title}

- {!!description &&

{description}

} -
- )} - {children} - {disabled && ( -
- )} -
-); - -const SettingsCardFooter = ({ children }) => ( -
- {children} -
-); - -const SettingsCardBody = ({ children }) => ( -
{children}
-); - -SettingsCard.Body = SettingsCardBody; -SettingsCard.Footer = SettingsCardFooter; - -export default SettingsCard; diff --git a/src/components/dashboard/sidebar/personal-account-menu.tsx b/src/components/dashboard/sidebar/personal-account-menu.tsx deleted file mode 100644 index 5595a87..0000000 --- a/src/components/dashboard/sidebar/personal-account-menu.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { Menu } from "react-daisyui"; -import Link from "next/link"; -import useTranslation from "next-translate/useTranslation"; -import { useRouter } from "next/router"; -import cx from "classnames"; -import { UseDashboardOverviewResponse } from "@/utils/api/use-dashboard-overview"; -import { useMemo } from "react"; -import { ACCOUNT_ROLES } from "@/types/auth"; - -type Props = { - currentAccount: UseDashboardOverviewResponse[0]; -}; -const PersonalAccountMenu = ({ currentAccount }: Props) => { - const { t } = useTranslation("dashboard"); - const router = useRouter(); - const menu = useMemo(() => { - const internal = [ - { - label: t("personalAccountMenu.dashboard"), - href: "/dashboard", - isActive: router.asPath === "/dashboard", - }, - { - label: t("personalAccountMenu.profile"), - href: "/dashboard/profile", - isActive: router.asPath === "/dashboard/profile", - }, - ]; - if (currentAccount?.account_role === ACCOUNT_ROLES.owner) { - internal.push({ - label: t("personalAccountMenu.billing"), - href: "/dashboard/billing", - isActive: router.asPath === "/dashboard/billing", - }); - } - return internal; - }, [router.asPath, currentAccount, t]); - return ( - - {menu.map((item) => ( - - - {item.label} - - - ))} - - ); -}; - -export default PersonalAccountMenu; diff --git a/src/components/dashboard/sidebar/profile-button.tsx b/src/components/dashboard/sidebar/profile-button.tsx deleted file mode 100644 index db32004..0000000 --- a/src/components/dashboard/sidebar/profile-button.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { Button, Dropdown } from "react-daisyui"; -import { useSupabaseClient, useUser } from "@supabase/auth-helpers-react"; - -import { Database } from "@/types/supabase-types"; -import Link from "next/link"; -import { useMemo } from "react"; -import { useRouter } from "next/router"; -import useTranslation from "next-translate/useTranslation"; -import useUserProfile from "@/utils/api/use-user-profile"; - -type Props = { - className?: string; -}; -const DashboardProfileButton = ({ className }: Props) => { - const { data: profile } = useUserProfile(); - const user = useUser(); - const { t } = useTranslation("dashboard"); - const supabaseClient = useSupabaseClient(); - const router = useRouter(); - - const menuButtonText = useMemo( - () => profile?.name || t("profileButton.yourAccount"), - [profile, t] - ); - return ( -
- - - -
-

{t("profileButton.loggedInAs")}

-

{user?.email}

-
- - {t("profileButton.editProfile")} - - { - await supabaseClient.auth.signOut(); - await router.push("/"); - }} - > - {t("shared.logOut")} - -
-
-
- ); -}; - -export default DashboardProfileButton; diff --git a/src/components/dashboard/sidebar/sidebar-menu.tsx b/src/components/dashboard/sidebar/sidebar-menu.tsx deleted file mode 100644 index 134dfed..0000000 --- a/src/components/dashboard/sidebar/sidebar-menu.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { Button } from "react-daisyui"; -import cx from "classnames"; -import { XIcon } from "@heroicons/react/outline"; -import ProfileButton from "@/components/dashboard/sidebar/profile-button"; -import ThemeSelector from "@/components/dashboard/sidebar/theme-selector"; -import TeamAccountMenu from "@/components/dashboard/sidebar/team-account-menu"; -import PersonalAccountMenu from "@/components/dashboard/sidebar/personal-account-menu"; -import TeamSelectMenu from "@/components/dashboard/sidebar/team-select-menu"; -import { UseDashboardOverviewResponse } from "@/utils/api/use-dashboard-overview"; -import Logo from "@/components/basejump-default-content/logo"; - -type Props = { - className?: string; - onClose?: () => void; - currentAccount?: UseDashboardOverviewResponse[0]; -}; -const SidebarMenu = ({ className, onClose, currentAccount }: Props) => { - return ( -
-
-
- - -
-
- -
- {currentAccount?.team_account === true ? ( - - ) : ( - - )} -
- -
- - -
-
- ); -}; - -export default SidebarMenu; diff --git a/src/components/dashboard/sidebar/team-account-menu.tsx b/src/components/dashboard/sidebar/team-account-menu.tsx deleted file mode 100644 index 8818dba..0000000 --- a/src/components/dashboard/sidebar/team-account-menu.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import { Menu } from "react-daisyui"; -import Link from "next/link"; -import useTranslation from "next-translate/useTranslation"; -import { useRouter } from "next/router"; -import cx from "classnames"; -import { useMemo } from "react"; -import { ACCOUNT_ROLES } from "@/types/auth"; -import { UseDashboardOverviewResponse } from "@/utils/api/use-dashboard-overview"; - -type Props = { - currentAccount: UseDashboardOverviewResponse[0]; -}; - -const TeamAccountMenu = ({ currentAccount }: Props) => { - const { t } = useTranslation("dashboard"); - const router = useRouter(); - const menu = useMemo(() => { - const items = [ - { - label: t("teamAccountMenu.dashboard"), - href: `/dashboard/teams/${currentAccount.account_id}`, - isActive: router.pathname === "/dashboard/teams/[accountId]", - }, - ]; - if (currentAccount.account_role === ACCOUNT_ROLES.owner) { - items.push({ - label: t("teamAccountMenu.settings"), - href: `/dashboard/teams/${currentAccount.account_id}/settings`, - isActive: router.pathname.includes( - "/dashboard/teams/[accountId]/settings" - ), - }); - } - return items; - }, [currentAccount, router.pathname, t]); - return ( - - {menu.map((item) => ( - - - {item.label} - - - ))} - - ); -}; - -export default TeamAccountMenu; diff --git a/src/components/dashboard/sidebar/team-select-menu.tsx b/src/components/dashboard/sidebar/team-select-menu.tsx deleted file mode 100644 index 4d6060a..0000000 --- a/src/components/dashboard/sidebar/team-select-menu.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import useTranslation from "next-translate/useTranslation"; -import { Button, Dropdown, Menu } from "react-daisyui"; -import { PlusIcon } from "@heroicons/react/solid"; -import { ChevronDownIcon } from "@heroicons/react/outline"; -import { DASHBOARD_PATH } from "@/types/auth"; -import { useToggle } from "react-use"; -import { useRouter } from "next/router"; -import Link from "next/link"; -import NewAccountModal from "@/components/dashboard/accounts/new-account-modal"; -import useDashboardOverview, { - UseDashboardOverviewResponse, -} from "@/utils/api/use-dashboard-overview"; -import { useMemo } from "react"; - -type Props = { - currentAccount: UseDashboardOverviewResponse[0]; -}; - -const TeamSelectMenu = ({ currentAccount }: Props) => { - const { t } = useTranslation("dashboard"); - const router = useRouter(); - const [newAccount, toggleNewAccount] = useToggle(false); - - const { data, refetch } = useDashboardOverview(); - - const personalAccount = useMemo( - () => data?.find((a) => a.personal_account), - [data] - ); - - const teamAccounts = useMemo( - () => data?.filter((a) => a.team_account), - [data] - ); - - async function onAccountCreated(accountId: string) { - await refetch(); - toggleNewAccount(false); - await router.push(`/dashboard/teams/${accountId}`); - } - - return ( - <> - - - - {!!personalAccount && ( - <> - -

{t("teamSelectMenu.personalAccount")}

-
- - {t("teamSelectMenu.myAccount")} - - - )} - -

{t("teamSelectMenu.teams")}

-
- {teamAccounts?.map((account) => ( - - {account.team_name} - - ))} - -
- -

{t("teamSelectMenu.newAccount")}

-
-
-
-
- - - ); -}; - -export default TeamSelectMenu; diff --git a/src/components/dashboard/sidebar/theme-selector.tsx b/src/components/dashboard/sidebar/theme-selector.tsx deleted file mode 100644 index 57c6a13..0000000 --- a/src/components/dashboard/sidebar/theme-selector.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { MoonIcon, SunIcon } from "@heroicons/react/outline"; -import { Button, Dropdown } from "react-daisyui"; -import { useMemo } from "react"; -import useThemeStorage from "@/utils/use-theme-storage"; - -export const AVAILABLE_THEMES = { - light: { - name: "Light", - id: "light", - Icon: SunIcon, - }, - dark: { - name: "Dark", - id: "dark", - Icon: MoonIcon, - }, - night: { - name: "Night", - id: "night", - Icon: MoonIcon, - }, - dracula: { - name: "Dracula", - id: "dracula", - Icon: MoonIcon, - }, -}; - -const ThemeSelector = () => { - const { theme, setTheme, clearTheme } = useThemeStorage(); - - const selectedTheme = useMemo(() => { - return AVAILABLE_THEMES[theme]; - }, [theme]); - - return ( - - - - {Object.values(AVAILABLE_THEMES).map((themeOption) => ( - - ))} - - - ); -}; - -export default ThemeSelector; diff --git a/src/components/docs/docs-layout.tsx b/src/components/docs/docs-layout.tsx deleted file mode 100644 index 5fc6eb0..0000000 --- a/src/components/docs/docs-layout.tsx +++ /dev/null @@ -1,48 +0,0 @@ -import DocsSidebar from "@/components/docs/docs-sidebar"; -import { ReactNode, useEffect } from "react"; -import { useToggle } from "react-use"; -import { Drawer } from "react-daisyui"; -import useTranslation from "next-translate/useTranslation"; -import { ChevronRightIcon } from "@heroicons/react/outline"; -import { useRouter } from "next/router"; - -type Props = { - navigation: any; - children: ReactNode; -}; - -const DocsLayout = ({ navigation, children }) => { - const [isSidebarOpen, toggleSidebar] = useToggle(false); - const { t } = useTranslation("content"); - const router = useRouter(); - - useEffect(() => { - toggleSidebar(false); - }, [router.asPath, toggleSidebar]); - - return ( - } - open={isSidebarOpen} - onClickOverlay={toggleSidebar} - > -
-
- -
- -
{children}
-
-
- ); -}; - -export default DocsLayout; diff --git a/src/components/docs/docs-sidebar.tsx b/src/components/docs/docs-sidebar.tsx deleted file mode 100644 index 341d2d8..0000000 --- a/src/components/docs/docs-sidebar.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import Link from "next/link"; -import { Menu } from "react-daisyui"; -import { GetDocsNavigationResponse } from "@/utils/content/content-helpers"; -import { Fragment } from "react"; -import { useRouter } from "next/router"; -import cx from "classnames"; -import useTranslation from "next-translate/useTranslation"; -import { XIcon } from "@heroicons/react/outline"; - -type Props = { - navigation: GetDocsNavigationResponse; - onClose?: () => void; -}; - -const DocsSidebar = ({ navigation, onClose }: Props) => { - const router = useRouter(); - const { t } = useTranslation("content"); - return ( -
- - - {navigation.rootPaths.map((rootPath) => ( - - - {rootPath.title} - - - ))} - {Object.keys(navigation.categories).map((category) => ( - - - {category} - - {navigation.categories[category].map((categoryPath) => ( - - - {categoryPath.title} - - - ))} - - ))} - -
- ); -}; - -export default DocsSidebar; diff --git a/src/middleware.ts b/src/middleware.ts deleted file mode 100644 index ff661bb..0000000 --- a/src/middleware.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { createMiddlewareSupabaseClient } from "@supabase/auth-helpers-nextjs"; -import type { NextRequest } from "next/server"; -import { NextResponse } from "next/server"; - -export async function middleware(req: NextRequest) { - // We need to create a response and hand it to the supabase client to be able to modify the response headers. - const res = NextResponse.next(); - - const supabase = await createMiddlewareSupabaseClient({ req, res }); - - const { - data: { session }, - } = await supabase.auth.getSession(); - - // Check auth condition - if (session) { - // Authentication successful, forward request to protected route. - return res; - } - - // Auth condition not met, redirect to home page. - const redirectUrl = req.nextUrl.clone(); - redirectUrl.pathname = - req.nextUrl.pathname === "/invitation" ? "/signup" : "/login"; - redirectUrl.searchParams.set(`redirectedFrom`, req.url); - return NextResponse.redirect(redirectUrl); -} - -export const config = { - matcher: ["/dashboard/:path*", "/invitation"], -}; diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx deleted file mode 100644 index b3980da..0000000 --- a/src/pages/_app.tsx +++ /dev/null @@ -1,56 +0,0 @@ -import "../styles/global.css"; -import "react-toastify/dist/ReactToastify.css"; -import type { AppProps } from "next/app"; -import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import ContentLayout from "../components/content-pages/content-layout"; -import DashboardLayout from "../components/dashboard/dashboard-layout"; -import { Theme } from "react-daisyui"; -import useThemeStorage from "@/utils/use-theme-storage"; -import { ToastContainer } from "react-toastify"; -import { useEffect, useState } from "react"; -import { createBrowserSupabaseClient } from "@supabase/auth-helpers-nextjs"; -import { SessionContextProvider } from "@supabase/auth-helpers-react"; -import { Database } from "@/types/supabase-types"; - -const queryClient = new QueryClient(); - -function MyApp({ Component, pageProps, router }: AppProps) { - const isDashboardPath = router.pathname.startsWith("/dashboard"); - const { theme } = useThemeStorage(); - const [supabaseClient] = useState(() => - createBrowserSupabaseClient() - ); - - useEffect(() => { - // our dropdowns are used for navigation a lot - // they work off css focus states, so they don't get removed - // on navigation transitions. this is a hack to force them to - const element = window?.document?.activeElement as HTMLElement; - if (typeof element?.blur === "function") { - element.blur(); - } - }, [router.asPath]); - return ( - - - - {isDashboardPath ? ( - - - - ) : ( - - - - )} - - - - - ); -} - -export default MyApp; diff --git a/src/pages/api/billing/portal-link.ts b/src/pages/api/billing/portal-link.ts deleted file mode 100644 index 60b059a..0000000 --- a/src/pages/api/billing/portal-link.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { withApiAuth } from "@supabase/auth-helpers-nextjs"; -import { NextApiRequest, NextApiResponse } from "next"; -import { createOrRetrieveCustomer } from "@/utils/admin/stripe-billing-helpers"; -import { stripe } from "@/utils/admin/stripe"; -import getFullRedirectUrl from "@/utils/get-full-redirect-url"; -import { ACCOUNT_ROLES } from "@/types/auth"; - -const createPortalLink = async ( - req: NextApiRequest, - res: NextApiResponse, - supabaseServerClient -) => { - if (req.method !== "POST") { - return res.status(405).json({ error: "Method Not Allowed" }); - } - const { accountId } = req.body; - - if (!accountId) { - return res.status(400).json({ error: "Missing account ID" }); - } - - const { data: currentUserRole } = await supabaseServerClient - .rpc("current_user_account_role", { - lookup_account_id: accountId, - }) - .single(); - - // only owners are allowed to update billing subscriptions - if (currentUserRole?.account_role !== ACCOUNT_ROLES.owner) { - return res.status(404).json({ error: "Account not found" }); - } - - try { - const { - data: { user }, - } = await supabaseServerClient.auth.getUser(); - const customer = await createOrRetrieveCustomer({ - accountId: accountId as string, - email: user.email || "", - }); - - if (!customer) throw Error("Could not get customer"); - const { url } = await stripe.billingPortal.sessions.create({ - customer, - return_url: getFullRedirectUrl( - currentUserRole?.is_personal_account - ? "/dashboard/billing" - : `/dashboard/teams/${accountId}/settings/billing` - ), - }); - - return res.status(200).json({ url }); - } catch (err: any) { - console.log(err); - res.status(500).json({ error: { statusCode: 500, message: err.message } }); - } -}; - -export default withApiAuth(createPortalLink); diff --git a/src/pages/api/billing/setup.ts b/src/pages/api/billing/setup.ts deleted file mode 100644 index b0fda0a..0000000 --- a/src/pages/api/billing/setup.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { NextApiRequest, NextApiResponse } from "next"; -import { stripe } from "@/utils/admin/stripe"; -import getFullRedirectUrl from "@/utils/get-full-redirect-url"; -import { withApiAuth } from "@supabase/auth-helpers-nextjs"; -import { ACCOUNT_ROLES } from "@/types/auth"; -import { createOrRetrieveCustomer } from "@/utils/admin/stripe-billing-helpers"; - -const BillingSetup = async ( - req: NextApiRequest, - res: NextApiResponse, - supabaseServerClient -) => { - if (req.method !== "POST") { - return res.status(405).json({ error: "Method Not Allowed" }); - } - - const { accountId, priceId } = req.body; - - if (!accountId) { - return res.status(400).json({ error: "Missing account ID" }); - } - - const { - data: { user }, - } = await supabaseServerClient.auth.getUser(); - - const { data: currentUserRole } = await supabaseServerClient - .rpc("current_user_account_role", { - lookup_account_id: accountId, - }) - .single(); - - // only owners are allowed to update billing subscriptions - if (currentUserRole?.account_role !== ACCOUNT_ROLES.owner) { - return res.status(404).json({ error: "Account not found" }); - } - - const customerId = await createOrRetrieveCustomer({ - accountId, - email: user.email, - }); - - const session = await stripe.checkout.sessions.create({ - payment_method_types: ["card"], - customer: customerId, - line_items: [{ price: priceId, quantity: 1 }], - mode: "subscription", - success_url: getFullRedirectUrl( - currentUserRole.is_personal_account - ? "/dashboard/billing" - : `/dashboard/teams/${accountId}/settings/billing` - ), - cancel_url: getFullRedirectUrl( - currentUserRole.is_personal_account - ? "/dashboard/billing" - : `/dashboard/teams/${accountId}/settings/billing` - ), - }); - res.status(200).json({ url: session.url }); -}; - -export default withApiAuth(BillingSetup); diff --git a/src/pages/api/billing/status.ts b/src/pages/api/billing/status.ts deleted file mode 100644 index 8a718d7..0000000 --- a/src/pages/api/billing/status.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { NextApiRequest, NextApiResponse } from "next"; -import { createOrRetrieveSubscription } from "@/utils/admin/stripe-billing-helpers"; -import { withApiAuth } from "@supabase/auth-helpers-nextjs"; -import { MANUAL_SUBSCRIPTION_REQUIRED } from "@/types/billing"; -import { supabaseAdmin } from "@/utils/admin/supabase-admin-client"; - -const BillingStatus = async ( - req: NextApiRequest, - res: NextApiResponse, - supabaseServerClient -) => { - const { accountId } = req.query; - - if (!accountId) { - return res.status(400).json({ error: "Missing account ID" }); - } - - const { - data: { user }, - } = await supabaseServerClient.auth.getUser(); - - const { data } = await supabaseServerClient - .rpc("current_user_account_role", { - lookup_account_id: accountId, - }) - .single(); - - const { data: config } = await supabaseAdmin - .rpc("get_service_role_config") - .single(); - - if (!data) { - return res.status(404).json({ error: "Account not found" }); - } - - // @ts-ignore - if (config.enable_account_billing === false) { - // If billing is disabled, return the account as active - return res.status(200).json({ - subscription_id: null, - subscription_active: true, - status: "active", - is_primary_owner: false, - billing_email: null, - plan_name: null, - account_role: data.account_role, - billing_enabled: false, - }); - } - - try { - const subscriptionData = await createOrRetrieveSubscription({ - accountId: accountId as string, - email: user.email, - }); - return res.status(200).json({ - subscription_id: subscriptionData.id, - subscription_active: ["trialing", "active"].includes( - subscriptionData.status - ), - plan_name: subscriptionData.plan_name, - billing_email: subscriptionData.billing_email, - status: subscriptionData.status, - account_role: data?.account_role, - is_primary_owner: data?.is_primary_owner, - billing_enabled: true, - }); - } catch (error) { - if (error.message === MANUAL_SUBSCRIPTION_REQUIRED) { - return res.status(200).json({ - subscription_id: null, - subscription_active: false, - plan_name: null, - billing_email: null, - status: MANUAL_SUBSCRIPTION_REQUIRED, - account_role: data?.account_role, - is_primary_owner: data?.is_primary_owner, - billing_enabled: true, - }); - } - return res.status(500).json({ error: error.message }); - } -}; - -export default withApiAuth(BillingStatus); diff --git a/src/pages/api/billing/stripe-webhooks.ts b/src/pages/api/billing/stripe-webhooks.ts deleted file mode 100644 index fb1bdf1..0000000 --- a/src/pages/api/billing/stripe-webhooks.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { stripe } from "@/utils/admin/stripe"; -import { - manageSubscriptionStatusChange, - upsertCustomerRecord, - upsertPriceRecord, - upsertProductRecord, -} from "@/utils/admin/stripe-billing-helpers"; -import { NextApiRequest, NextApiResponse } from "next"; -import Stripe from "stripe"; -import { Readable } from "node:stream"; - -// Stripe requires the raw body to construct the event. -export const config = { - api: { - bodyParser: false, - }, -}; - -async function buffer(readable: Readable) { - const chunks = []; - for await (const chunk of readable) { - chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk); - } - return Buffer.concat(chunks); -} - -const relevantEvents = new Set([ - "product.created", - "product.updated", - "price.created", - "price.updated", - "checkout.session.completed", - "customer.subscription.created", - "customer.subscription.updated", - "customer.subscription.deleted", - "customer.created", - "customer.updated", - "customer.deleted", -]); - -const stripeWebhookHandler = async ( - req: NextApiRequest, - res: NextApiResponse -) => { - if (req.method !== "POST") { - res.setHeader("Allow", "POST"); - res.status(405).end("Method not allowed"); - return; - } - - const buf = await buffer(req); - const sig = req.headers["stripe-signature"]; - const webhookSecret = process.env.STRIPE_WEBHOOK_SECRET; - let event: Stripe.Event; - - try { - if (!sig || !webhookSecret) return; - event = stripe.webhooks.constructEvent(buf, sig, webhookSecret); - } catch (err: any) { - console.log(`❌ Error message: ${err.message}`); - return res.status(400).send(`Webhook Error: ${err.message}`); - } - - if (relevantEvents.has(event.type)) { - try { - switch (event.type) { - case "customer.created": - case "customer.updated": - case "customer.deleted": - await upsertCustomerRecord(event.data.object as Stripe.Customer); - break; - case "product.created": - case "product.updated": - await upsertProductRecord(event.data.object as Stripe.Product); - break; - case "price.created": - case "price.updated": - await upsertPriceRecord(event.data.object as Stripe.Price); - break; - case "customer.subscription.created": - case "customer.subscription.updated": - case "customer.subscription.deleted": - const subscription = event.data.object as Stripe.Subscription; - await manageSubscriptionStatusChange( - subscription.id, - subscription.customer as string - ); - break; - case "checkout.session.completed": - const checkoutSession = event.data.object as Stripe.Checkout.Session; - if (checkoutSession.mode === "subscription") { - const subscriptionId = checkoutSession.subscription; - await manageSubscriptionStatusChange( - subscriptionId as string, - checkoutSession.customer as string - ); - } - break; - default: - throw new Error("Unhandled relevant event!"); - } - } catch (error) { - console.log(error); - return res - .status(400) - .send('Webhook error: "Webhook handler failed. View logs."'); - } - } - - res.json({ received: true }); -}; - -export default stripeWebhookHandler; diff --git a/src/pages/api/og.tsx b/src/pages/api/og.tsx deleted file mode 100644 index 6428728..0000000 --- a/src/pages/api/og.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import { ImageResponse } from "@vercel/og"; -import { NextRequest } from "next/server"; - -export const config = { - runtime: "experimental-edge", -}; - -export default function SocialImage(req: NextRequest) { - try { - const { searchParams } = new URL(req.url); - - const hasTitle = searchParams.has("title"); - const title = hasTitle ? searchParams.get("title")?.slice(0, 100) : ""; - console.log("title found", title); - return new ImageResponse( - ( -
- {title} -
- ), - { - width: 1200, - height: 600, - } - ); - } catch (e) { - console.log(`${e.message}`); - return new Response(`Failed to generate an image`, { - status: 500, - }); - } -} diff --git a/src/pages/blog/[...slug].tsx b/src/pages/blog/[...slug].tsx deleted file mode 100644 index 3ff711b..0000000 --- a/src/pages/blog/[...slug].tsx +++ /dev/null @@ -1,80 +0,0 @@ -import { - getContentBySlug, - getContentPaths, -} from "@/utils/content/content-helpers"; -import { MDXRemote } from "next-mdx-remote"; -import { serialize } from "next-mdx-remote/serialize"; -import Link from "next/link"; -import useTranslation from "next-translate/useTranslation"; -import ContentMeta from "@/components/content-pages/content-meta"; - -const BlogShow = ({ content, title, meta }) => { - const { t } = useTranslation("content"); - return ( -
- -
-
    -
  • - - {t("home")} - -
  • -
  • - - {t("blog")} - -
  • -
  • {title}
  • -
-
- {!!meta?.category && ( - {meta.category} - )} -

{title}

- -
- ); -}; - -export default BlogShow; - -export async function getStaticProps({ params, locale, ...rest }) { - const blog = await getContentBySlug(params.slug?.[0], { - locale, - contentType: "blog", - }); - - const content = await serialize(blog.content); - return { - props: { - ...blog, - content, - }, - }; -} - -export async function getStaticPaths({ locales }) { - const paths = []; - for (const locale of locales) { - const filePaths = await getContentPaths(locale, "blog"); - filePaths.forEach((filePath) => { - paths.push({ - params: { - slug: [filePath.slug], - }, - locale, - }); - }); - } - - return { - paths, - fallback: false, - }; -} diff --git a/src/pages/blog/index.tsx b/src/pages/blog/index.tsx deleted file mode 100644 index baf36c3..0000000 --- a/src/pages/blog/index.tsx +++ /dev/null @@ -1,57 +0,0 @@ -import { getContentPaths } from "@/utils/content/content-helpers"; -import ContentMeta from "@/components/content-pages/content-meta"; -import useTranslation from "next-translate/useTranslation"; -import Link from "next/link"; -import { MDXRemote } from "next-mdx-remote"; -import { serialize } from "next-mdx-remote/serialize"; - -const BlogIndex = ({ articles }) => { - const { t } = useTranslation("content"); - return ( -
- -
- {articles.map((article) => ( -
- {!!article.meta?.category && ( - - {article.meta.category} - - )} - -

{article.title}

- - -
- ))} -
-
- ); -}; - -export default BlogIndex; - -export async function getStaticProps({ params, locale, ...rest }) { - const content = await getContentPaths(locale, "blog"); - const articles = []; - for (const article of content) { - articles.push({ - ...article, - content: await serialize(article.content), - }); - } - return { - props: { - articles: articles.reverse(), - }, - }; -} diff --git a/src/pages/dashboard/billing.tsx b/src/pages/dashboard/billing.tsx deleted file mode 100644 index 9bc9bd2..0000000 --- a/src/pages/dashboard/billing.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import DashboardContent from "@/components/dashboard/shared/dashboard-content"; -import useTranslation from "next-translate/useTranslation"; -import AccountSubscription from "@/components/dashboard/accounts/settings/account-subscription"; -import usePersonalAccount from "@/utils/api/use-personal-account"; -import DashboardMeta from "@/components/dashboard/dashboard-meta"; - -const PersonalAccountBilling = () => { - const { t } = useTranslation("dashboard"); - const { data } = usePersonalAccount(); - return ( - - - {t("billing.pageTitle")} - - - - - ); -}; - -export default PersonalAccountBilling; diff --git a/src/pages/dashboard/index.tsx b/src/pages/dashboard/index.tsx deleted file mode 100644 index 3fa9e99..0000000 --- a/src/pages/dashboard/index.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import Loader from "@/components/core/loader"; -import usePersonalAccount from "@/utils/api/use-personal-account"; -import PersonalAccountDeactivated from "@/components/dashboard/accounts/personal-account-deactivated"; -import FutureContentPlaceholder from "@/components/basejump-default-content/future-content-placeholder"; -import useTranslation from "next-translate/useTranslation"; -import DashboardMeta from "@/components/dashboard/dashboard-meta"; - -const DashboardIndex = () => { - const { data: personalAccount, isLoading } = usePersonalAccount(); - const { t } = useTranslation("dashboard"); - /** - * This page does the heavy lifting for handling the fact that - * Basejump supports personal accounts, team accounts and a combination - * of both. If no personal account is loaded, it means that personal - * accounts are deactivated. In that case, we show current teams and - * prompt them to create one if none exist. If a personal account is - * loaded, we show the personal account dashboard page - */ - - return ( - <> - - {isLoading ? ( - - ) : !personalAccount ? ( - // Personal accounts are deactivated, so we - // prompt the user to jump to a team dashboard -
- -
- ) : ( - // Replace me with your content! - - )} - - ); -}; - -export default DashboardIndex; diff --git a/src/pages/dashboard/profile.tsx b/src/pages/dashboard/profile.tsx deleted file mode 100644 index 88b537d..0000000 --- a/src/pages/dashboard/profile.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import UpdateProfileName from "@/components/dashboard/profile/update-profile-name"; -import DashboardContent from "@/components/dashboard/shared/dashboard-content"; -import UpdateEmailAddress from "@/components/dashboard/profile/update-email-address"; -import useTranslation from "next-translate/useTranslation"; -import ListTeams from "@/components/dashboard/profile/list-teams"; -import DashboardMeta from "@/components/dashboard/dashboard-meta"; - -const DashboardProfile = () => { - const { t } = useTranslation("dashboard"); - return ( - - - {t("profile.pageTitle")} - -
- - - -
-
-
- ); -}; - -export default DashboardProfile; diff --git a/src/pages/dashboard/teams/[accountId]/index.tsx b/src/pages/dashboard/teams/[accountId]/index.tsx deleted file mode 100644 index f43dfe3..0000000 --- a/src/pages/dashboard/teams/[accountId]/index.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import FutureContentPlaceholder from "@/components/basejump-default-content/future-content-placeholder"; -import { useRouter } from "next/router"; -import useTeamAccount from "@/utils/api/use-team-account"; -import useTranslation from "next-translate/useTranslation"; -import DashboardMeta from "@/components/dashboard/dashboard-meta"; - -const DashboardTeamIndex = () => { - const router = useRouter(); - const { accountId } = router.query; - const { data } = useTeamAccount(accountId as string); - const { t } = useTranslation("dashboard"); - return ( - <> - - - - ); -}; - -export default DashboardTeamIndex; diff --git a/src/pages/dashboard/teams/[accountId]/settings/billing.tsx b/src/pages/dashboard/teams/[accountId]/settings/billing.tsx deleted file mode 100644 index 51603cb..0000000 --- a/src/pages/dashboard/teams/[accountId]/settings/billing.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import AccountSettingsLayout from "@/components/dashboard/accounts/settings/account-settings-layout"; -import AccountSubscription from "@/components/dashboard/accounts/settings/account-subscription"; -import { useRouter } from "next/router"; -import useTeamAccount from "@/utils/api/use-team-account"; -import useTranslation from "next-translate/useTranslation"; -import DashboardMeta from "@/components/dashboard/dashboard-meta"; - -const TeamSettingsBilling = () => { - const router = useRouter(); - const { accountId } = router.query; - const { data } = useTeamAccount(accountId as string); - const { t } = useTranslation("dashboard"); - return ( - - - - - ); -}; - -export default TeamSettingsBilling; diff --git a/src/pages/dashboard/teams/[accountId]/settings/index.tsx b/src/pages/dashboard/teams/[accountId]/settings/index.tsx deleted file mode 100644 index bff33a8..0000000 --- a/src/pages/dashboard/teams/[accountId]/settings/index.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import AccountSettingsLayout from "@/components/dashboard/accounts/settings/account-settings-layout"; -import UpdateAccountName from "@/components/dashboard/accounts/settings/update-account-name"; -import { useRouter } from "next/router"; -import useTeamAccount from "@/utils/api/use-team-account"; -import useTranslation from "next-translate/useTranslation"; -import DashboardMeta from "@/components/dashboard/dashboard-meta"; - -const DashboardTeamSettingsIndex = () => { - const router = useRouter(); - const { accountId } = router.query; - const { data } = useTeamAccount(accountId as string); - const { t } = useTranslation("dashboard"); - return ( - - - - - ); -}; - -export default DashboardTeamSettingsIndex; diff --git a/src/pages/dashboard/teams/[accountId]/settings/members.tsx b/src/pages/dashboard/teams/[accountId]/settings/members.tsx deleted file mode 100644 index a61147d..0000000 --- a/src/pages/dashboard/teams/[accountId]/settings/members.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import AccountSettingsLayout from "@/components/dashboard/accounts/settings/account-settings-layout"; -import SettingsCard from "@/components/dashboard/shared/settings-card"; -import useTranslation from "next-translate/useTranslation"; -import InviteMember from "@/components/dashboard/accounts/settings/invite-member"; -import { useRouter } from "next/router"; -import ListMembers from "@/components/dashboard/accounts/settings/list-team-members"; -import ListTeamInvitations from "@/components/dashboard/accounts/settings/list-team-invitations"; -import useTeamInvitations from "@/utils/api/use-team-invitations"; -import useTeamAccount from "@/utils/api/use-team-account"; -import DashboardMeta from "@/components/dashboard/dashboard-meta"; - -const TeamSettingsMembers = () => { - const { t } = useTranslation("dashboard"); - const router = useRouter(); - const { accountId } = router.query; - const { refetch } = useTeamInvitations(accountId as string); - const { data } = useTeamAccount(accountId as string); - return ( - - -
- -
- refetch()} - /> -
- -
- -
-
- ); -}; - -export default TeamSettingsMembers; diff --git a/src/pages/docs/[...slug].tsx b/src/pages/docs/[...slug].tsx deleted file mode 100644 index 70dd7a7..0000000 --- a/src/pages/docs/[...slug].tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { - getContentBySlug, - getContentPaths, - getDocsNavigation, -} from "@/utils/content/content-helpers"; -import { MDXRemote } from "next-mdx-remote"; -import { serialize } from "next-mdx-remote/serialize"; -import DocsLayout from "@/components/docs/docs-layout"; -import ContentMeta from "@/components/content-pages/content-meta"; - -const DocsShow = ({ navigation, content, title, meta }) => { - return ( - - -
- {!!meta?.category && ( - - {meta.category} - - )} -

{title}

- -
-
- ); -}; - -export default DocsShow; - -export async function getStaticProps({ params, locale, ...rest }) { - const doc = await getContentBySlug(params.slug?.[0], { - locale, - contentType: "docs", - }); - - const navigation = await getDocsNavigation(locale); - - const content = await serialize(doc.content); - return { - props: { - ...doc, - navigation, - content, - }, - }; -} - -export async function getStaticPaths({ locales }) { - const paths = []; - for (const locale of locales) { - const filePaths = await getContentPaths(locale, "docs"); - filePaths - .filter( - // Resolves an issue where returning empty paths collides with the index page - // known issue: https://github.com/vercel/next.js/issues/12717 - (filePath) => !filePath.slug.includes("index") && filePath.slug !== "" - ) - .forEach((filePath) => { - paths.push({ - params: { - slug: [filePath.slug], - }, - locale, - }); - }); - } - - return { - paths, - fallback: false, - }; -} diff --git a/src/pages/docs/index.tsx b/src/pages/docs/index.tsx deleted file mode 100644 index 27bc0c5..0000000 --- a/src/pages/docs/index.tsx +++ /dev/null @@ -1,45 +0,0 @@ -import { - getContentBySlug, - getDocsNavigation, -} from "@/utils/content/content-helpers"; -import { MDXRemote } from "next-mdx-remote"; -import { serialize } from "next-mdx-remote/serialize"; -import DocsLayout from "@/components/docs/docs-layout"; -import ContentMeta from "@/components/content-pages/content-meta"; - -const DocsIndex = ({ navigation, content, title, meta }) => { - return ( - - -
-

{title}

- -
-
- ); -}; - -export default DocsIndex; - -export async function getStaticProps({ params, locale, ...rest }) { - const doc = await getContentBySlug("index", { - locale, - contentType: "docs", - }); - - const navigation = await getDocsNavigation(locale); - - const content = await serialize(doc.content); - return { - props: { - ...doc, - navigation, - content, - }, - }; -} diff --git a/src/pages/index.tsx b/src/pages/index.tsx deleted file mode 100644 index 2143823..0000000 --- a/src/pages/index.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import BasejumpHomepage from "@/components/basejump-default-content/homepage"; - -const IndexPage = () => ; - -export default IndexPage; diff --git a/src/pages/invitation.tsx b/src/pages/invitation.tsx deleted file mode 100644 index 5b3b1eb..0000000 --- a/src/pages/invitation.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import { useRouter } from "next/router"; -import useInvitation from "@/utils/api/use-invitation"; -import useTranslation from "next-translate/useTranslation"; -import { Button } from "react-daisyui"; -import { useMutation } from "@tanstack/react-query"; -import handleSupabaseErrors from "@/utils/handle-supabase-errors"; -import Loader from "@/components/core/loader"; -import { useSupabaseClient } from "@supabase/auth-helpers-react"; -import { Database } from "@/types/supabase-types"; - -const Invitation = () => { - const router = useRouter(); - const { token } = router.query; - const { t } = useTranslation("dashboard"); - const supabaseClient = useSupabaseClient(); - - const { data, isLoading } = useInvitation(token as string); - - const acceptInvitation = useMutation( - async (invitationToken: string) => { - const { data, error } = await supabaseClient.rpc("accept_invitation", { - lookup_invitation_token: invitationToken, - }); - - handleSupabaseErrors(data, error); - return data; - }, - { - onSuccess(accountId) { - router.push(`/dashboard/teams/${accountId}`); - }, - } - ); - - return ( -
- {isLoading ? ( - - ) : !data?.active ? ( -

{t("invitation.invalid")}

- ) : ( - <> -

{t("invitation.title")}

-

{data?.team_name}

- - - )} -
- ); -}; - -export default Invitation; diff --git a/src/pages/login.tsx b/src/pages/login.tsx deleted file mode 100644 index 74d9747..0000000 --- a/src/pages/login.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import LoginPassword from "@/components/dashboard/authentication/login-password"; -import useTranslation from "next-translate/useTranslation"; -import Link from "next/link"; -import { useRouter } from "next/router"; -import useAuthCheck from "@/utils/use-auth-check"; - -const LoginPage = () => { - const { t } = useTranslation("authentication"); - const router = useRouter(); - const { redirectedFrom } = router.query; - - useAuthCheck(redirectedFrom as string); - - return ( -
- - - {t("shared.notYetRegistered")} - -
- ); -}; - -export default LoginPage; diff --git a/src/pages/signup.tsx b/src/pages/signup.tsx deleted file mode 100644 index 63ddae4..0000000 --- a/src/pages/signup.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import SignupPassword from "@/components/dashboard/authentication/signup-password"; -import Link from "next/link"; -import useTranslation from "next-translate/useTranslation"; -import { useRouter } from "next/router"; -import useAuthCheck from "@/utils/use-auth-check"; - -const SignUpPage = () => { - const { t } = useTranslation("authentication"); - const router = useRouter(); - const { redirectedFrom } = router.query; - - useAuthCheck(redirectedFrom as string); - return ( -
- - - {t("shared.alreadyRegistered")} - -
- ); -}; - -export default SignUpPage; diff --git a/src/styles/global.css b/src/styles/global.css deleted file mode 100644 index bd6213e..0000000 --- a/src/styles/global.css +++ /dev/null @@ -1,3 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; \ No newline at end of file diff --git a/src/types/auth.ts b/src/types/auth.ts deleted file mode 100644 index a12cb8c..0000000 --- a/src/types/auth.ts +++ /dev/null @@ -1,8 +0,0 @@ -export const LOGIN_PATH = "/login"; -export const REGISTER_PATH = "/signup"; -export const DASHBOARD_PATH = "/dashboard"; - -export enum ACCOUNT_ROLES { - owner = "owner", - member = "member", -} diff --git a/src/types/billing.ts b/src/types/billing.ts deleted file mode 100644 index f5c1cfc..0000000 --- a/src/types/billing.ts +++ /dev/null @@ -1 +0,0 @@ -export const MANUAL_SUBSCRIPTION_REQUIRED = "manual_subscription_required"; diff --git a/src/types/supabase-types.ts b/src/types/supabase-types.ts deleted file mode 100644 index 7592ad6..0000000 --- a/src/types/supabase-types.ts +++ /dev/null @@ -1,468 +0,0 @@ -export type Json = - | string - | number - | boolean - | null - | { [key: string]: Json } - | Json[]; - -export interface Database { - graphql_public: { - Tables: { - [_ in never]: never; - }; - Views: { - [_ in never]: never; - }; - Functions: { - graphql: { - Args: { - operationName: string; - query: string; - variables: Json; - extensions: Json; - }; - Returns: Json; - }; - }; - Enums: { - [_ in never]: never; - }; - }; - public: { - Tables: { - account_user: { - Row: { - account_id: string; - account_role: Database["public"]["Enums"]["account_role"]; - user_id: string; - }; - Insert: { - account_id: string; - account_role: Database["public"]["Enums"]["account_role"]; - user_id: string; - }; - Update: { - account_id?: string; - account_role?: Database["public"]["Enums"]["account_role"]; - user_id?: string; - }; - }; - accounts: { - Row: { - created_at: string | null; - id: string; - personal_account: boolean; - primary_owner_user_id: string; - team_name: string | null; - updated_at: string | null; - }; - Insert: { - created_at?: string | null; - id?: string; - personal_account?: boolean; - primary_owner_user_id?: string; - team_name?: string | null; - updated_at?: string | null; - }; - Update: { - created_at?: string | null; - id?: string; - personal_account?: boolean; - primary_owner_user_id?: string; - team_name?: string | null; - updated_at?: string | null; - }; - }; - billing_customers: { - Row: { - account_id: string; - active: boolean | null; - customer_id: string | null; - email: string | null; - provider: Database["public"]["Enums"]["billing_providers"] | null; - }; - Insert: { - account_id: string; - active?: boolean | null; - customer_id?: string | null; - email?: string | null; - provider?: Database["public"]["Enums"]["billing_providers"] | null; - }; - Update: { - account_id?: string; - active?: boolean | null; - customer_id?: string | null; - email?: string | null; - provider?: Database["public"]["Enums"]["billing_providers"] | null; - }; - }; - billing_prices: { - Row: { - active: boolean | null; - billing_product_id: string | null; - currency: string | null; - description: string | null; - id: string; - interval: Database["public"]["Enums"]["pricing_plan_interval"] | null; - interval_count: number | null; - metadata: Json | null; - provider: Database["public"]["Enums"]["billing_providers"] | null; - trial_period_days: number | null; - type: Database["public"]["Enums"]["pricing_type"] | null; - unit_amount: number | null; - }; - Insert: { - active?: boolean | null; - billing_product_id?: string | null; - currency?: string | null; - description?: string | null; - id: string; - interval?: - | Database["public"]["Enums"]["pricing_plan_interval"] - | null; - interval_count?: number | null; - metadata?: Json | null; - provider?: Database["public"]["Enums"]["billing_providers"] | null; - trial_period_days?: number | null; - type?: Database["public"]["Enums"]["pricing_type"] | null; - unit_amount?: number | null; - }; - Update: { - active?: boolean | null; - billing_product_id?: string | null; - currency?: string | null; - description?: string | null; - id?: string; - interval?: - | Database["public"]["Enums"]["pricing_plan_interval"] - | null; - interval_count?: number | null; - metadata?: Json | null; - provider?: Database["public"]["Enums"]["billing_providers"] | null; - trial_period_days?: number | null; - type?: Database["public"]["Enums"]["pricing_type"] | null; - unit_amount?: number | null; - }; - }; - billing_products: { - Row: { - active: boolean | null; - description: string | null; - id: string; - image: string | null; - metadata: Json | null; - name: string | null; - provider: Database["public"]["Enums"]["billing_providers"] | null; - }; - Insert: { - active?: boolean | null; - description?: string | null; - id: string; - image?: string | null; - metadata?: Json | null; - name?: string | null; - provider?: Database["public"]["Enums"]["billing_providers"] | null; - }; - Update: { - active?: boolean | null; - description?: string | null; - id?: string; - image?: string | null; - metadata?: Json | null; - name?: string | null; - provider?: Database["public"]["Enums"]["billing_providers"] | null; - }; - }; - billing_subscriptions: { - Row: { - account_id: string; - cancel_at: string | null; - cancel_at_period_end: boolean | null; - canceled_at: string | null; - created: string; - current_period_end: string; - current_period_start: string; - ended_at: string | null; - id: string; - metadata: Json | null; - price_id: string | null; - provider: Database["public"]["Enums"]["billing_providers"] | null; - quantity: number | null; - status: Database["public"]["Enums"]["subscription_status"] | null; - trial_end: string | null; - trial_start: string | null; - }; - Insert: { - account_id: string; - cancel_at?: string | null; - cancel_at_period_end?: boolean | null; - canceled_at?: string | null; - created?: string; - current_period_end?: string; - current_period_start?: string; - ended_at?: string | null; - id: string; - metadata?: Json | null; - price_id?: string | null; - provider?: Database["public"]["Enums"]["billing_providers"] | null; - quantity?: number | null; - status?: Database["public"]["Enums"]["subscription_status"] | null; - trial_end?: string | null; - trial_start?: string | null; - }; - Update: { - account_id?: string; - cancel_at?: string | null; - cancel_at_period_end?: boolean | null; - canceled_at?: string | null; - created?: string; - current_period_end?: string; - current_period_start?: string; - ended_at?: string | null; - id?: string; - metadata?: Json | null; - price_id?: string | null; - provider?: Database["public"]["Enums"]["billing_providers"] | null; - quantity?: number | null; - status?: Database["public"]["Enums"]["subscription_status"] | null; - trial_end?: string | null; - trial_start?: string | null; - }; - }; - invitations: { - Row: { - account_id: string; - account_role: Database["public"]["Enums"]["account_role"]; - account_team_name: string | null; - created_at: string | null; - id: string; - invitation_type: Database["public"]["Enums"]["invitation_type"]; - invited_by_user_id: string; - token: string; - updated_at: string | null; - }; - Insert: { - account_id: string; - account_role: Database["public"]["Enums"]["account_role"]; - account_team_name?: string | null; - created_at?: string | null; - id?: string; - invitation_type: Database["public"]["Enums"]["invitation_type"]; - invited_by_user_id: string; - token?: string; - updated_at?: string | null; - }; - Update: { - account_id?: string; - account_role?: Database["public"]["Enums"]["account_role"]; - account_team_name?: string | null; - created_at?: string | null; - id?: string; - invitation_type?: Database["public"]["Enums"]["invitation_type"]; - invited_by_user_id?: string; - token?: string; - updated_at?: string | null; - }; - }; - profiles: { - Row: { - created_at: string | null; - id: string; - name: string | null; - updated_at: string | null; - }; - Insert: { - created_at?: string | null; - id: string; - name?: string | null; - updated_at?: string | null; - }; - Update: { - created_at?: string | null; - id?: string; - name?: string | null; - updated_at?: string | null; - }; - }; - }; - Views: { - [_ in never]: never; - }; - Functions: { - accept_invitation: { - Args: { lookup_invitation_token: string }; - Returns: string; - }; - current_user_account_role: { - Args: { lookup_account_id: string }; - Returns: Json; - }; - get_account_billing_status: { - Args: { lookup_account_id: string }; - Returns: Json; - }; - get_service_role_config: { - Args: Record; - Returns: Json; - }; - lookup_invitation: { - Args: { lookup_invitation_token: string }; - Returns: Json; - }; - update_account_user_role: { - Args: { - account_id: string; - user_id: string; - new_account_role: Database["public"]["Enums"]["account_role"]; - make_primary_owner: boolean; - }; - Returns: undefined; - }; - }; - Enums: { - account_role: "owner" | "member"; - billing_providers: "stripe"; - invitation_type: "one-time" | "24-hour"; - pricing_plan_interval: "day" | "week" | "month" | "year"; - pricing_type: "one_time" | "recurring"; - subscription_status: - | "trialing" - | "active" - | "canceled" - | "incomplete" - | "incomplete_expired" - | "past_due" - | "unpaid"; - }; - }; - storage: { - Tables: { - buckets: { - Row: { - created_at: string | null; - id: string; - name: string; - owner: string | null; - public: boolean | null; - updated_at: string | null; - }; - Insert: { - created_at?: string | null; - id: string; - name: string; - owner?: string | null; - public?: boolean | null; - updated_at?: string | null; - }; - Update: { - created_at?: string | null; - id?: string; - name?: string; - owner?: string | null; - public?: boolean | null; - updated_at?: string | null; - }; - }; - migrations: { - Row: { - executed_at: string | null; - hash: string; - id: number; - name: string; - }; - Insert: { - executed_at?: string | null; - hash: string; - id: number; - name: string; - }; - Update: { - executed_at?: string | null; - hash?: string; - id?: number; - name?: string; - }; - }; - objects: { - Row: { - bucket_id: string | null; - created_at: string | null; - id: string; - last_accessed_at: string | null; - metadata: Json | null; - name: string | null; - owner: string | null; - path_tokens: string[] | null; - updated_at: string | null; - }; - Insert: { - bucket_id?: string | null; - created_at?: string | null; - id?: string; - last_accessed_at?: string | null; - metadata?: Json | null; - name?: string | null; - owner?: string | null; - path_tokens?: string[] | null; - updated_at?: string | null; - }; - Update: { - bucket_id?: string | null; - created_at?: string | null; - id?: string; - last_accessed_at?: string | null; - metadata?: Json | null; - name?: string | null; - owner?: string | null; - path_tokens?: string[] | null; - updated_at?: string | null; - }; - }; - }; - Views: { - [_ in never]: never; - }; - Functions: { - extension: { - Args: { name: string }; - Returns: string; - }; - filename: { - Args: { name: string }; - Returns: string; - }; - foldername: { - Args: { name: string }; - Returns: string[]; - }; - get_size_by_bucket: { - Args: Record; - Returns: { size: number; bucket_id: string }[]; - }; - search: { - Args: { - prefix: string; - bucketname: string; - limits: number; - levels: number; - offsets: number; - search: string; - sortcolumn: string; - sortorder: string; - }; - Returns: { - name: string; - id: string; - updated_at: string; - created_at: string; - last_accessed_at: string; - metadata: Json; - }[]; - }; - }; - Enums: { - [_ in never]: never; - }; - }; -} diff --git a/src/utils/admin/stripe-billing-helpers.ts b/src/utils/admin/stripe-billing-helpers.ts deleted file mode 100644 index b0d4fdb..0000000 --- a/src/utils/admin/stripe-billing-helpers.ts +++ /dev/null @@ -1,288 +0,0 @@ -import { stripe } from "./stripe"; -import fromUnixTime from "date-fns/fromUnixTime"; -import Stripe from "stripe"; -import { supabaseAdmin } from "@/utils/admin/supabase-admin-client"; -import { MANUAL_SUBSCRIPTION_REQUIRED } from "@/types/billing"; - -enum BILLING_PROVIDERS { - stripe = "stripe", -} - -/** - * Products are defined in stripe, this handler takes a stripe product and - * makes sure we have a local copy for pricing pages - * @param product Stripe product object - */ -const upsertProductRecord = async (product: Stripe.Product) => { - const productData = { - id: product.id, - active: product.active, - name: product.name, - description: product.description ?? undefined, - image: product.images?.[0] ?? null, - metadata: product.metadata, - provider: BILLING_PROVIDERS.stripe, - }; - - const { error } = await supabaseAdmin - .from("billing_products") - .upsert([productData]); - if (error) throw error; - console.log(`Product inserted/updated: ${product.id}`); -}; - -/** - * Prices are defined in stripe and connected to a product - * Products typically have 1-2 prices assigned to them, but can have unlimited - * @param price - */ -const upsertPriceRecord = async (price: Stripe.Price) => { - const priceData = { - id: price.id, - billing_product_id: typeof price.product === "string" ? price.product : "", - active: price.active, - currency: price.currency, - description: price.nickname ?? undefined, - type: price.type, - unit_amount: price.unit_amount ?? undefined, - interval: price.recurring?.interval, - interval_count: price.recurring?.interval_count, - trial_period_days: price.recurring?.trial_period_days, - metadata: price.metadata, - provider: BILLING_PROVIDERS.stripe, - }; - - const { error } = await supabaseAdmin - .from("billing_prices") - .upsert([priceData]); - if (error) throw error; - console.log(`Price inserted/updated: ${price.id}`); -}; - -/** - * This is the customer object inside of stripe. It should map 1:1 with accounts in most cases - * It does NOT map back to users - * @param customer - * @param accountId - */ -const upsertCustomerRecord = async ( - customer: Stripe.Customer, - accountId?: string -) => { - const customerData = { - account_id: accountId || customer.metadata.account_id, - customer_id: customer.id, - email: customer.email, - provider: BILLING_PROVIDERS.stripe, - }; - - const { error } = await supabaseAdmin - .from("billing_customers") - .upsert([customerData]); - if (error) throw error; - console.log(`Customer inserted/updated: ${customer.id}`); -}; - -/** - * Convenience function that checks if a stripe customer with a given email address already exists - * in our database. If it doesn't, it creates a new one - * @param accountId - */ -const createOrRetrieveCustomer = async ({ - accountId, - email, -}: { - accountId: string; - email: string; -}) => { - const { data, error } = await supabaseAdmin - .from("billing_customers") - .select("customer_id") - .eq("account_id", accountId) - .single(); - if (error) { - // No customer record found, let's create one. - const customerData: { metadata: { account_id: string }; email?: string } = { - metadata: { - account_id: accountId, - }, - }; - - if (email) { - customerData.email = email; - } - const customer = await stripe.customers.create(customerData); - // now we upsert the customer record. Upsert b/c the stripe webhook also hits this and so there could be - // a race condition - await upsertCustomerRecord(customer, accountId); - console.log(`New customer created and inserted for ${accountId}.`); - return customer.id; - } - if (data) return data.customer_id; -}; - -/** - * Searches for an existing subscription connect to an accountID. - * If it exists, it returns the subscription ID and status - * if it doesn't exist, it loads the billing customer ID and then creates a new subscription - * @param accountId - */ - -type CreateOrRetrieveSubscriptionResponse = { - id: string; - status: string; - plan_name?: string; - billing_email?: string; -}; - -const createOrRetrieveSubscription = async ({ - accountId, - email, -}: { - accountId: string; - email: string; -}): Promise => { - const { data, error } = await supabaseAdmin - .rpc("get_account_billing_status", { lookup_account_id: accountId }) - .single(); - - if (!error && data) { - return { - id: data["id"], - status: data["status"], - billing_email: data["billing_email"], - plan_name: data["plan_name"], - }; - } - - // No subscription found, let's create one. - const customerId = await createOrRetrieveCustomer({ accountId, email }); - // search for the subscription to see if it's already been created - // this would only happen in weird race conditions or past errors, but worth confirming - const subscription = await stripe.subscriptions.list({ - customer: customerId, - limit: 1, - }); - - if (subscription.data.length === 1) { - // we found a subscription, let's upsert it - await upsertSubscriptionRecord(subscription.data[0], accountId); - // now that we've upserted it, we want to re-call the function - return createOrRetrieveSubscription({ accountId, email }); - } - try { - // load up the default billing config we want to use - const { data: config } = await supabaseAdmin - .rpc("get_service_role_config") - .single(); - - const { data: price } = await supabaseAdmin - .from("billing_prices") - .select("unit_amount") - .eq("id", config["stripe_default_account_price_id"]) - .single(); - - if ( - !price || - (price.unit_amount > 0 && !config["stripe_default_trial_period_days"]) - ) { - throw new Error(MANUAL_SUBSCRIPTION_REQUIRED); - } - - const newSubscription = await stripe.subscriptions.create({ - customer: customerId, - items: [{ price: config["stripe_default_account_price_id"] }], - expand: ["latest_invoice.payment_intent"], - trial_period_days: config["stripe_default_trial_period_days"], - }); - // now we upsert the subscription record. Upsert b/c the stripe webhook also hits this and so there could b - await upsertSubscriptionRecord(newSubscription, accountId); - return createOrRetrieveSubscription({ accountId, email }); - } catch (error) { - throw error; - } -}; - -/** - * Takes a stripe subscription object and upserts it into our database - * @param subscription Stripe.Subscription - * @param accountId string - */ -const upsertSubscriptionRecord = async ( - subscription: Stripe.Subscription, - accountId: string -) => { - const subscriptionData = { - id: subscription.id, - account_id: accountId, - metadata: subscription.metadata, - status: subscription.status, - price_id: subscription.items.data[0].price.id, - quantity: subscription.items.data[0].quantity, - cancel_at_period_end: subscription.cancel_at_period_end, - cancel_at: subscription.cancel_at - ? fromUnixTime(subscription.cancel_at).toISOString() - : null, - canceled_at: subscription.canceled_at - ? fromUnixTime(subscription.canceled_at).toISOString() - : null, - current_period_start: fromUnixTime(subscription.current_period_start).toISOString(), - current_period_end: fromUnixTime(subscription.current_period_end).toISOString(), - created: fromUnixTime(subscription.created).toISOString(), - ended_at: subscription.ended_at - ? fromUnixTime(subscription.ended_at).toISOString() - : null, - trial_start: subscription.trial_start - ? fromUnixTime(subscription.trial_start).toISOString() - : null, - trial_end: subscription.trial_end - ? fromUnixTime(subscription.trial_end).toISOString() - : null, - provider: BILLING_PROVIDERS.stripe, - }; - - const { error } = await supabaseAdmin - .from("billing_subscriptions") - .upsert(subscriptionData); - if (error) throw error; - console.log( - `Inserted/updated subscription [${subscription.id}] for account [${accountId}]` - ); -}; - -/** - * Subscriptions are the primary tracking for an accounts status within the app. - * This takes a stripe subscription event and upserts it into our database so that - * we have a local version of an accounts current status - * @param subscriptionId - * @param customerId - * @param accountCreated Is this an account created event? - */ -const manageSubscriptionStatusChange = async ( - subscriptionId: string, - customerId: string -) => { - // Get customer's UUID from mapping table. - const { data: customerData, error: noCustomerError } = await supabaseAdmin - .from("billing_customers") - .select("account_id") - .eq("customer_id", customerId) - .single(); - if (noCustomerError) throw noCustomerError; - - const subscription = await stripe.subscriptions.retrieve(subscriptionId, { - expand: ["default_payment_method"], - }); - // Upsert the latest status of the subscription object. - await upsertSubscriptionRecord(subscription, customerData.account_id); -}; - -export { - upsertProductRecord, - upsertPriceRecord, - upsertSubscriptionRecord, - createOrRetrieveCustomer, - createOrRetrieveSubscription, - upsertCustomerRecord, - manageSubscriptionStatusChange, -}; diff --git a/src/utils/admin/stripe.ts b/src/utils/admin/stripe.ts deleted file mode 100644 index 5a9bc01..0000000 --- a/src/utils/admin/stripe.ts +++ /dev/null @@ -1,12 +0,0 @@ -import Stripe from "stripe"; - -export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY || "", { - // https://github.com/stripe/stripe-node#configuration - apiVersion: "2022-08-01", - // Register this as an official Stripe plugin. - // https://stripe.com/docs/building-plugins#setappinfo - appInfo: { - name: `Basejump App`, - version: "0.1.0", - }, -}); diff --git a/src/utils/admin/supabase-admin-client.ts b/src/utils/admin/supabase-admin-client.ts deleted file mode 100644 index 4519500..0000000 --- a/src/utils/admin/supabase-admin-client.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { createClient } from "@supabase/supabase-js"; -import { Database } from "@/types/supabase-types"; - -export const supabaseAdmin = createClient( - process.env.NEXT_PUBLIC_SUPABASE_URL || "", - process.env.SUPABASE_SERVICE_ROLE_KEY || "" -); diff --git a/src/utils/api/use-account-billing-options.ts b/src/utils/api/use-account-billing-options.ts deleted file mode 100644 index a35e2da..0000000 --- a/src/utils/api/use-account-billing-options.ts +++ /dev/null @@ -1,60 +0,0 @@ -import { useSupabaseClient, useUser } from "@supabase/auth-helpers-react"; -import { useQuery, UseQueryOptions } from "@tanstack/react-query"; -import handleSupabaseErrors from "@/utils/handle-supabase-errors"; -import { Database } from "@/types/supabase-types"; - -export type UseAccountBillingOptionsResponse = Array<{ - product_name: string; - product_description: string; - currency: string; - price: number; - price_id: string; - interval: string; -}>; - -/** - * Get a given accounts account-subscription-takeover status. Returns "missing" if it has not yet been setup. - * @param accountId - * @param options - */ -export default function useAccountBillingOptions( - accountId: string, - options?: UseQueryOptions -) { - const user = useUser(); - const supabaseClient = useSupabaseClient(); - return useQuery( - ["accountBillingOptions", accountId], - async () => { - const { data, error } = await supabaseClient - .from("billing_products") - .select( - "name, description, billing_prices (currency, unit_amount, id, interval, type)" - ); - - handleSupabaseErrors(data, error); - - const results = []; - - data?.forEach((product) => { - // @ts-ignore - product.billing_prices?.forEach((price) => { - results.push({ - product_name: product.name, - product_description: product.description, - currency: price.currency, - price: price.unit_amount, - price_id: price.id, - interval: price.type === "one_time" ? "one_time" : price.interval, - }); - }); - }); - - return results; - }, - { - ...options, - enabled: !!accountId && !!user && !!supabaseClient, - } - ); -} diff --git a/src/utils/api/use-account-billing-status.ts b/src/utils/api/use-account-billing-status.ts deleted file mode 100644 index cffd56b..0000000 --- a/src/utils/api/use-account-billing-status.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { useSessionContext, useUser } from "@supabase/auth-helpers-react"; -import { useQuery, UseQueryOptions } from "@tanstack/react-query"; -import { Database } from "@/types/supabase-types"; - -type UseAccountBillingStatusResponse = { - subscription_id: string; - subscription_active: boolean; - status: string; - is_primary_owner: boolean; - billing_email?: string; - plan_name?: string; - account_role: Database["public"]["Tables"]["account_user"]["Row"]["account_role"]; - billing_enabled: boolean; -}; - -/** - * Get a given accounts billing status. Returns "missing" if it has not yet been setup. - * @param accountId - * @param options - */ -export default function useAccountBillingStatus( - accountId: string, - options?: UseQueryOptions -) { - const user = useUser(); - const { supabaseClient } = useSessionContext(); - return useQuery( - ["accountBillingStatus", accountId], - async () => { - const response = await fetch( - `/api/billing/status?accountId=${accountId}` - ); - if (!response.ok) { - throw new Error(response.statusText); - } - const data = await response.json(); - return data; - }, - { - ...options, - enabled: !!accountId && !!user && !!supabaseClient, - } - ); -} diff --git a/src/utils/api/use-dashboard-overview.ts b/src/utils/api/use-dashboard-overview.ts deleted file mode 100644 index 729bcde..0000000 --- a/src/utils/api/use-dashboard-overview.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { useSupabaseClient, useUser } from "@supabase/auth-helpers-react"; -import { useQuery, UseQueryOptions } from "@tanstack/react-query"; -import handleSupabaseErrors from "../handle-supabase-errors"; -import { Database } from "@/types/supabase-types"; - -export type UseDashboardOverviewResponse = { - team_name: string; - account_id: string; - account_role: Database["public"]["Tables"]["account_user"]["Row"]["account_role"]; - subscription_active: boolean; - subscription_status: Database["public"]["Tables"]["billing_subscriptions"]["Row"]["status"]; - personal_account: boolean; - team_account: boolean; -}[]; - -export default function useDashboardOverview( - options?: UseQueryOptions -) { - const user = useUser(); - const supabaseClient = useSupabaseClient(); - return useQuery( - ["dashboardOverview", user?.id], - async () => { - const { data, error } = await supabaseClient - .from("accounts") - .select( - "team_name, id, personal_account, billing_subscriptions (status), account_user!inner(account_role)" - ) - .eq("account_user.user_id", user?.id); - - handleSupabaseErrors(data, error); - - return data?.map((account) => ({ - team_name: account.team_name, - account_id: account.id, - account_role: account.account_user?.[0]?.account_role, - subscription_active: ["active", "trialing"].includes( - account.billing_subscriptions?.[0]?.status - ), - subscription_status: account.billing_subscriptions?.[0]?.status, - personal_account: account.personal_account, - team_account: !account.personal_account, - })); - }, - { - ...options, - enabled: !!user && !!supabaseClient, - } - ); -} diff --git a/src/utils/api/use-invitation.ts b/src/utils/api/use-invitation.ts deleted file mode 100644 index 96eba60..0000000 --- a/src/utils/api/use-invitation.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { useSupabaseClient, useUser } from "@supabase/auth-helpers-react"; -import { useQuery, UseQueryOptions } from "@tanstack/react-query"; -import handleSupabaseErrors from "../handle-supabase-errors"; -import { Database } from "@/types/supabase-types"; - -type Invitation = { - team_name?: string; - active: boolean; -}; -export default function useInvitation( - invitationToken: string, - options?: UseQueryOptions -) { - const user = useUser(); - const supabaseClient = useSupabaseClient(); - return useQuery( - ["invitation", invitationToken], - async () => { - const { data, error } = await supabaseClient.rpc("lookup_invitation", { - lookup_invitation_token: invitationToken, - }); - handleSupabaseErrors(data, error); - - return data as unknown as Invitation; - }, - { - ...options, - enabled: - Boolean(invitationToken) && Boolean(user) && Boolean(supabaseClient), - } - ); -} diff --git a/src/utils/api/use-personal-account.ts b/src/utils/api/use-personal-account.ts deleted file mode 100644 index 6d7bc03..0000000 --- a/src/utils/api/use-personal-account.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { useSessionContext, useUser } from "@supabase/auth-helpers-react"; -import { useQuery, UseQueryOptions } from "@tanstack/react-query"; -import handleSupabaseErrors from "../handle-supabase-errors"; -import { Database } from "@/types/supabase-types"; - -export default function usePersonalAccount( - options?: UseQueryOptions -) { - const user = useUser(); - const { supabaseClient } = useSessionContext(); - return useQuery( - ["personalAccount", user?.id], - async () => { - const { data, error } = await supabaseClient - .from("accounts") - .select() - .eq("primary_owner_user_id", user?.id) - .eq("personal_account", true) - .maybeSingle(); - handleSupabaseErrors(data, error); - return data; - }, - { - ...options, - enabled: !!user && !!supabaseClient, - } - ); -} diff --git a/src/utils/api/use-team-account.ts b/src/utils/api/use-team-account.ts deleted file mode 100644 index 139b0b3..0000000 --- a/src/utils/api/use-team-account.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { useQuery, UseQueryOptions } from "@tanstack/react-query"; -import handleSupabaseErrors from "../handle-supabase-errors"; -import { Database } from "@/types/supabase-types"; -import { useSupabaseClient } from "@supabase/auth-helpers-react"; - -export default function useTeamAccount( - accountId: string, - options?: UseQueryOptions -) { - const supabaseClient = useSupabaseClient(); - return useQuery( - ["teamAccount", accountId], - async () => { - const { data, error } = await supabaseClient - .from("accounts") - .select() - .eq("id", accountId) - .single(); - handleSupabaseErrors(data, error); - return data; - }, - { - ...options, - enabled: !!accountId && !!supabaseClient, - } - ); -} diff --git a/src/utils/api/use-team-accounts.ts b/src/utils/api/use-team-accounts.ts deleted file mode 100644 index aaaea97..0000000 --- a/src/utils/api/use-team-accounts.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { useSessionContext, useUser } from "@supabase/auth-helpers-react"; -import { useQuery, UseQueryOptions } from "@tanstack/react-query"; -import handleSupabaseErrors from "../handle-supabase-errors"; -import { Database } from "@/types/supabase-types"; - -type TeamAccountWithRole = Database["public"]["Tables"]["accounts"]["Row"] & { - account_role: string; -}; - -export default function useTeamAccounts( - options?: UseQueryOptions -) { - const user = useUser(); - const { supabaseClient } = useSessionContext(); - return useQuery( - ["teamAccounts", user?.id], - async () => { - const { data, error } = await supabaseClient - .from("account_user") - .select("account_role, account:account_id (*)") - .eq("user_id", user?.id); - handleSupabaseErrors(data, error); - - return data - ?.filter((a) => a.account?.personal_account === false) - ?.map(({ account_role, account }) => ({ - ...account, - account_role, - })); - }, - { - ...options, - enabled: !!user && !!supabaseClient, - } - ); -} diff --git a/src/utils/api/use-team-invitations.ts b/src/utils/api/use-team-invitations.ts deleted file mode 100644 index 493c7d2..0000000 --- a/src/utils/api/use-team-invitations.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { useSessionContext, useUser } from "@supabase/auth-helpers-react"; -import { useQuery, UseQueryOptions } from "@tanstack/react-query"; -import handleSupabaseErrors from "../handle-supabase-errors"; -import { Database } from "@/types/supabase-types"; - -export default function useTeamInvitations( - accountId: string, - options?: UseQueryOptions< - Database["public"]["Tables"]["invitations"]["Row"][] - > -) { - const user = useUser(); - const { supabaseClient } = useSessionContext(); - return useQuery( - ["teamInvitations", accountId], - async () => { - const { data, error } = await supabaseClient - .from("invitations") - .select("*") - .order("created_at", { ascending: false }) - .match({ account_id: accountId }); - handleSupabaseErrors(data, error); - - return data; - }, - { - ...options, - enabled: !!accountId && !!user && !!supabaseClient, - } - ); -} diff --git a/src/utils/api/use-team-members.ts b/src/utils/api/use-team-members.ts deleted file mode 100644 index 7582b78..0000000 --- a/src/utils/api/use-team-members.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { useSessionContext, useUser } from "@supabase/auth-helpers-react"; -import { useQuery, UseQueryOptions } from "@tanstack/react-query"; -import handleSupabaseErrors from "../handle-supabase-errors"; -import { Database } from "@/types/supabase-types"; - -export type UseTeamMembersResponse = { - user_id: string; - account_id: string; - is_primary_owner: boolean; - account_role: Database["public"]["Tables"]["account_user"]["Row"]["account_role"]; - name: string; -}; - -export default function useTeamMembers( - accountId: string, - options?: UseQueryOptions -) { - const user = useUser(); - const { supabaseClient } = useSessionContext(); - return useQuery( - ["teamMembers", accountId], - async () => { - const { data, error } = await supabaseClient - .from("account_user") - .select("*, profiles(name), accounts(primary_owner_user_id)") - .match({ account_id: accountId }); - handleSupabaseErrors(data, error); - - return data?.map(({ account_role, user_id, profiles, accounts }) => ({ - account_role, - account_id: accountId, - name: profiles.name, - is_primary_owner: accounts.primary_owner_user_id === user_id, - user_id, - })); - }, - { - ...options, - enabled: !!accountId && !!user && !!supabaseClient, - } - ); -} diff --git a/src/utils/api/use-team-role.ts b/src/utils/api/use-team-role.ts deleted file mode 100644 index 6877ffc..0000000 --- a/src/utils/api/use-team-role.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { useSupabaseClient, useUser } from "@supabase/auth-helpers-react"; -import { useQuery, UseQueryOptions } from "@tanstack/react-query"; -import handleSupabaseErrors from "../handle-supabase-errors"; -import { Database } from "@/types/supabase-types"; - -export type UseTeamRoleResponse = { - is_primary_owner: boolean; - account_role: Database["public"]["Tables"]["account_user"]["Row"]["account_role"]; -}; - -/** - * Get the current user's role in a team - * @param accountId - * @param options - */ -export default function useTeamRole( - accountId: string, - options?: UseQueryOptions -) { - const user = useUser(); - const supabaseClient = useSupabaseClient(); - const { data, isLoading } = useQuery( - ["teamRole", accountId], - async () => { - const { data, error } = await supabaseClient - .rpc("current_user_account_role", { - lookup_account_id: accountId, - }) - .single(); - handleSupabaseErrors(data, error); - - return data; - }, - { - ...options, - enabled: !!accountId && !!user && !!supabaseClient, - } - ); - - return { - accountRole: data?.account_role, - isPrimaryOwner: data?.is_primary_owner, - isLoading, - }; -} diff --git a/src/utils/api/use-user-profile.ts b/src/utils/api/use-user-profile.ts deleted file mode 100644 index 086203a..0000000 --- a/src/utils/api/use-user-profile.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { useSessionContext, useUser } from "@supabase/auth-helpers-react"; -import { useQuery } from "@tanstack/react-query"; -import handleSupabaseErrors from "@/utils/handle-supabase-errors"; -import { Database } from "@/types/supabase-types"; - -export default function useUserProfile() { - const user = useUser(); - const { supabaseClient } = useSessionContext(); - return useQuery( - ["userProfile", user?.id], - async () => { - const { data, error } = await supabaseClient - .from("profiles") - .select("*") - .eq("id", user?.id) - .single(); - handleSupabaseErrors(data, error); - return data; - }, - { - enabled: !!user && !!supabaseClient, - } - ); -} diff --git a/src/utils/content/content-helpers.ts b/src/utils/content/content-helpers.ts deleted file mode 100644 index 9fae252..0000000 --- a/src/utils/content/content-helpers.ts +++ /dev/null @@ -1,155 +0,0 @@ -import matter from "gray-matter"; -import { join } from "path"; -import { readdirSync, readFileSync } from "fs"; -import { slugToTitle } from "@/utils/content/slug-to-title"; -import { compareAsc, isBefore } from "date-fns"; - -type ContentTypes = "docs" | "blog"; - -/** - * Next requires a json-serializable object to be returned from getStaticProps - * So we have to cleanup the metadata that gets returned a bit - * @param filePath - */ -function loadFileWithMeta(filePath: string) { - const file = readFileSync(filePath, "utf8"); - const { data: meta, content } = matter(file); - return { meta: JSON.parse(JSON.stringify(meta)), content }; -} - -/** - * We want to load files from the content directory for production, or from the test directory for tests - * We do this so that after the project template is copied somewhere else things don't start failing - * once people add their own docs/blogs - */ -const basePath = - process.env.NODE_ENV === "test" ? "__tests__/content" : "content"; - -type ContentPathsObject = { - slug: string; - fullPath: string; - title: string; - content: string; - meta: { - [key: string]: any; - }; -}; - -type ContentPathsResponse = Array; - -/** - * Generates a list of content paths for a given content type and locale - * @param locale - * @param contentType - */ -export async function getContentPaths( - locale: string, - contentType: ContentTypes -): Promise { - const files = readdirSync(join(process.cwd(), basePath, contentType, locale)); - return ( - files - .map((filePath) => { - // clean up markdown extension and replace index files with a non-index slug - const slug = filePath.replace(/\.md$/, "").replace(/\/?index$/, ""); - const { meta, content } = loadFileWithMeta( - join(process.cwd(), basePath, contentType, locale, filePath) - ); - - return { - slug, - fullPath: join("/", contentType, slug), - meta, - title: meta?.title || slugToTitle(slug), - content, - }; - }) - // remove unpublished files - .filter( - (file) => - file.meta?.published && - isBefore(new Date(file.meta.published), new Date()) - ) - // sort by publish date - .sort((fileA, fileB) => { - return compareAsc( - new Date(fileA.meta.published), - new Date(fileB.meta.published) - ); - }) - ); -} - -type GetContentBySlugResponse = { - slug: string; - title: string; - meta: { - [key: string]: any; - }; - content: string; -}; - -/** - * Gets the full content object for a given content type, locale and slug - * @param slug - * @param locale - * @param contentType - */ -export async function getContentBySlug( - slug: string, - { locale, contentType }: { locale: string; contentType: ContentTypes } -): Promise { - /** - * We've probably been given a url slug, but it could be a file path - * so we normalize it here. - */ - const realSlug = slug.replace(/\.md$/, ""); - const fullPath = join( - process.cwd(), - basePath, - contentType, - locale, - `${realSlug}.md` - ); - const { meta, content } = loadFileWithMeta(fullPath); - - return { - slug, - meta, - content, - title: meta?.title || slugToTitle(slug), - }; -} - -export type GetDocsNavigationResponse = { - rootPaths: Array; - categories: { - [key: string]: Array; - }; -}; - -/** - * Returns a navigation object to be used when rendering the docs navigation. - * This one does not apply to blogs - * @param locale - */ -export async function getDocsNavigation( - locale: string -): Promise { - const navigation = { - rootPaths: [], - categories: {}, - }; - - const filePaths = await getContentPaths(locale, "docs"); - filePaths.forEach((filePath) => { - if (filePath.meta?.category) { - navigation.categories[filePath.meta.category] ||= []; - navigation.categories[filePath.meta.category].push(filePath); - } else { - navigation.rootPaths.push(filePath); - } - }); - - return navigation; -} diff --git a/src/utils/content/slug-to-title.ts b/src/utils/content/slug-to-title.ts deleted file mode 100644 index 2f5a756..0000000 --- a/src/utils/content/slug-to-title.ts +++ /dev/null @@ -1,7 +0,0 @@ -export function slugToTitle(slug: string): string { - if (!slug) return ""; - return slug - ?.split(/[-_]/gi) - .map((s) => s[0].toUpperCase() + s.slice(1)) - .join(" "); -} diff --git a/src/utils/content/use-header-navigation.ts b/src/utils/content/use-header-navigation.ts deleted file mode 100644 index a0d1881..0000000 --- a/src/utils/content/use-header-navigation.ts +++ /dev/null @@ -1,15 +0,0 @@ -import useTranslation from "next-translate/useTranslation"; - -export default function useHeaderNavigation() { - const { t } = useTranslation("content"); - return [ - { - title: t("docs"), - href: "/docs", - }, - { - title: t("blog"), - href: "/blog", - }, - ]; -} diff --git a/src/utils/get-full-redirect-url.ts b/src/utils/get-full-redirect-url.ts deleted file mode 100644 index 67a2014..0000000 --- a/src/utils/get-full-redirect-url.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default function getFullRedirectUrl(redirectPath: string) { - if (redirectPath.startsWith("http")) return redirectPath; - const baseUrl = process.env.URL || window.location.origin; - return [baseUrl, redirectPath?.replace(/^\//, "")].filter(Boolean).join("/"); -} diff --git a/src/utils/get-invitation-url.ts b/src/utils/get-invitation-url.ts deleted file mode 100644 index e9148ef..0000000 --- a/src/utils/get-invitation-url.ts +++ /dev/null @@ -1,4 +0,0 @@ -export default function getInvitationUrl(invitationToken: string) { - const baseUrl = process.env.URL || window.location.origin; - return `${baseUrl}/invitation?token=${invitationToken}`; -} diff --git a/src/utils/handle-supabase-errors.ts b/src/utils/handle-supabase-errors.ts deleted file mode 100644 index 4913fc3..0000000 --- a/src/utils/handle-supabase-errors.ts +++ /dev/null @@ -1,5 +0,0 @@ -export default function handleSupabaseErrors(data: any, error: any) { - if (error) { - throw new Error(error.message); - } -} diff --git a/src/utils/use-auth-check.ts b/src/utils/use-auth-check.ts deleted file mode 100644 index b6e28c0..0000000 --- a/src/utils/use-auth-check.ts +++ /dev/null @@ -1,23 +0,0 @@ -import { useRouter } from "next/router"; -import { useSessionContext } from "@supabase/auth-helpers-react"; -import { useEffect } from "react"; - -/** - * There's an issue with Supabase Auth where oauth and magic links don't work with middleware - * on the initial page load. This is because the token is sent over a hash param which - * is not sent to the server. This hook sits client side, checks for an authenticated session on the login - * page, and redirects the user if it's found. - * @param redirectedFrom - */ -const useAuthCheck = (redirectedFrom?: string) => { - const { replace } = useRouter(); - const { session } = useSessionContext(); - - useEffect(() => { - if (session && redirectedFrom) { - replace(redirectedFrom as string); - } - }, [session, redirectedFrom, replace]); -}; - -export default useAuthCheck; diff --git a/src/utils/use-theme-storage.ts b/src/utils/use-theme-storage.ts deleted file mode 100644 index 611781d..0000000 --- a/src/utils/use-theme-storage.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useLocalStorage } from "react-use"; -import { useTheme } from "react-daisyui"; -import { useEffect } from "react"; - -const THEME_STORAGE_KEY = "basejump-theme"; -export default function useThemeStorage(defaultTheme?: string) { - const [value, setValue, remove] = useLocalStorage( - THEME_STORAGE_KEY, - defaultTheme - ); - const { theme, setTheme } = useTheme(value); - - useEffect(() => { - setTheme(value); - }, [value, setTheme]); - - function setInternalTheme(theme: string) { - setValue(theme); - } - - return { theme, setTheme: setInternalTheme, clearTheme: remove }; -} diff --git a/supabase/.gitignore b/supabase/.gitignore index 773c7c3..b86860c 100644 --- a/supabase/.gitignore +++ b/supabase/.gitignore @@ -1,3 +1,5 @@ -# Supabase .branches .temp +config.toml +functions/.env +seed.sql diff --git a/supabase/config.toml b/supabase/config.toml deleted file mode 100644 index 2018bc0..0000000 --- a/supabase/config.toml +++ /dev/null @@ -1,82 +0,0 @@ -# A string used to distinguish different Supabase projects on the same host. Defaults to the working -# directory name when running `supabase init`. -project_id = "basejump" - -[api] -# Port to use for the API URL. -port = 54321 -# Schemas to expose in your API. Tables, views and stored procedures in this schema will get API -# endpoints. public and storage are always included. -schemas = ["public", "storage", "graphql_public"] -# Extra schemas to add to the search_path of every request. public is always included. -extra_search_path = ["public", "extensions"] -# The maximum number of rows returns from a view, table, or stored procedure. Limits payload size -# for accidental or malicious requests. -max_rows = 1000 - -[db] -# Port to use for the local database URL. -port = 54322 -# The database major version to use. This has to be the same as your remote database's. Run `SHOW -# server_version;` on the remote database to check. -major_version = 15 - -[studio] -# Port to use for Supabase Studio. -port = 54323 - -# Email testing server. Emails sent with the local dev setup are not actually sent - rather, they -# are monitored, and you can view the emails that would have been sent from the web interface. -[inbucket] -# Port to use for the email testing server web interface. -port = 54324 -smtp_port = 54325 -pop3_port = 54326 - -[storage] -# The maximum file size allowed (e.g. "5MB", "500KB"). -file_size_limit = "50MiB" - -[auth] -# The base URL of your website. Used as an allow-list for redirects and for constructing URLs used -# in emails. -site_url = "http://localhost:3000" -# A list of *exact* URLs that auth providers are permitted to redirect to post authentication. -additional_redirect_urls = ["https://localhost:3000"] -# How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 seconds (one -# week). -jwt_expiry = 3600 -# Allow/disallow new user signups to your project. -enable_signup = true - -[auth.email] -# Allow/disallow new user signups via email to your project. -enable_signup = true -# If enabled, a user will be required to confirm any email change on both the old, and new email -# addresses. If disabled, only the new email is required to confirm. -double_confirm_changes = false -# If enabled, users need to confirm their email address before signing in. -enable_confirmations = false - -# Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`, -# `discord`, `facebook`, `github`, `gitlab`, `google`, `keycloak`, `linkedin`, `notion`, `twitch`, -# `twitter`, `slack`, `spotify`, `workos`, `zoom`. -[auth.external.apple] -enabled = false -client_id = "" -secret = "" -# Overrides the default auth redirectUrl. -redirect_uri = "" -# Overrides the default auth provider URL. Used to support self-hosted gitlab, single-tenant Azure, -# or any other third-party OIDC providers. -url = "" - -[analytics] -enabled = false -port = 54327 -vector_port = 54328 -# Setup BigQuery project to enable log viewer on local development stack. -# See: https://logflare.app/guides/bigquery-setup -gcp_project_id = "" -gcp_project_number = "" -gcp_jwt_path = "supabase/gcloud.json" diff --git a/supabase/functions/.env.example b/supabase/functions/.env.example new file mode 100644 index 0000000..6cebac1 --- /dev/null +++ b/supabase/functions/.env.example @@ -0,0 +1,4 @@ +STRIPE_API_KEY=asdf +STRIPE_WEBHOOK_SIGNING_SECRET=asdf +STRIPE_DEFAULT_PLAN_ID=asdf +STRIPE_DEFAULT_TRIAL_DAYS=7 \ No newline at end of file diff --git a/supabase/functions/deno-packages/billing-functions/README.md b/supabase/functions/deno-packages/billing-functions/README.md new file mode 100644 index 0000000..e76c4ef --- /dev/null +++ b/supabase/functions/deno-packages/billing-functions/README.md @@ -0,0 +1,175 @@ +# Basejump Edge Function Core + +Convenience functions for working with Supabase Edge Functions alongside Basejump. + +### Billing Endpoint + +All the functionality needed to enable revenue generating on accounts within Basejump. + +```typescript +import {serve} from "https://deno.land/std@0.168.0/http/server.ts"; +import { + billingEndpoint, + stripeBillingEndpoint +} from "https://raw.githubusercontent.com/usebasejump/basejump/main/deno-packages/billing-functions/mod.ts"; + +const stripeResponse = stripeBillingEndpoint({ + stripeClient, +}); + +serve(async (req) => { + const response = await billingEndpoint(req, stripeResponse); + return response; +}); +``` + +### Billing Webhooks + +Webhook consumer for events from your billing provider. + +```typescript +import {serve} from "https://deno.land/std@0.168.0/http/server.ts"; +import { + billingWebhookEndpoint, + stripeBillingWebhookEndpoint +} from "https://raw.githubusercontent.com/usebasejump/basejump/main/deno-packages/billing-functions/mod.ts"; + +const stripeResponse = stripeBillingWebhookEndpoint({ + stripeClient, +}); + +serve(async (req) => { + const response = await billingWebhookEndpoint(req, stripeResponse); + return response; +}); +``` + +### Requiring an account member / owner + +Convenience function useful for ensuring a user should have access to a specific function. + +```typescript +import {serve} from "https://deno.land/std@0.168.0/http/server.ts"; +import { + requireAccountMember +} from "https://raw.githubusercontent.com/usebasejump/basejump/main/deno-packages/billing-functions/mod.ts"; + +serve(async (req) => { + if (req.method === "OPTIONS") { + return new Response("ok", {headers: corsHeaders}); + } + + const body = await req.json(); + + const response = await requireAccountMember(req, { + accountId: body.account_id, + allowedRoles: ["owner", "member"], + onBlocked: + () => new Response("Unauthorized", {status: 401}), + onAuthorized: + async (supabaseClient) => { + return new Response("Authorized", {status: 200}); + }, + onError: + (error) => new Response(error.message, {status: 500}), + }) + ; + + return response; +}); + +``` + +# Creating your own Billing adapters + +Out of the box, Basejump provides support for a Stripe billing adapter. But there's no reason you can't provide your own +adapters for other services, such as Lemon Squeezy. + +Billing adapters should implement a client facing Edge function for handling requests as well as a webhook edge function +for keeping subscriptions up to date. + +## Custom Billing Edge function + +An Edge Function responsible for handling specific requests from the client applications. + +```typescript + +export default function customBillingEndpoint(config) { + return { + async getPlans({ + accountId, subscriptionId, customerId + }) { + // Get available plans from your provider + return { + plans: [ + { + id: "plan_123", + name: "Plan 123", + description: "Plan 123", + amount: 1000, + currency: "usd", + interval: "month", + interval_count: 1, + trial_period_days: 30, + metadata: {}, + }, + ], + }; + }, + async getBillingPortalUrl({accountId, subscriptionId, customerId}) { + // load the billing portal url from your provider + return { + url: "https://example.com/billing-portal", + }; + }, + async getNewSubscriptionUrl({accountId, email, planId}) { + // load the new subscription url from your provider + return { + url: "https://example.com/new-subscription", + }; + }, + async getBillingStatus({accountId, subscriptionId, customerId}) { + // load the billing status from your provider + return { + customer: { + id: "cus_123", + email: "test@test.com" + }, + subscription: { + id: "sub_123", + status: "active", + plan_name: "Plan 123", + ... + } + }; + }, + }; +} +``` + +## Custom Billing Webhook function + +Responsible for receiving webhooks from the billing platform and updating customer/subscription objects + +```typescript + +export default function customBillingWebhookEndpoint(config) { + return { + async handleEvent(req) { + // handle the event from your provider + return { + customer: { + id: "cus_123", + email: "test@test.com" + }, + subscription: { + id: "sub_123", + status: "active", + plan_name: "Plan 123", + ... + } + } + } + }; +} +``` \ No newline at end of file diff --git a/supabase/functions/deno-packages/billing-functions/deps.ts b/supabase/functions/deno-packages/billing-functions/deps.ts new file mode 100644 index 0000000..e99356b --- /dev/null +++ b/supabase/functions/deno-packages/billing-functions/deps.ts @@ -0,0 +1,2 @@ +export { createClient as createSupabaseClient } from "https://esm.sh/@supabase/supabase-js@2"; +export * as Stripe from "https://esm.sh/stripe@11.1.0?target=deno"; diff --git a/supabase/functions/deno-packages/billing-functions/lib/cors-headers.ts b/supabase/functions/deno-packages/billing-functions/lib/cors-headers.ts new file mode 100644 index 0000000..02b087a --- /dev/null +++ b/supabase/functions/deno-packages/billing-functions/lib/cors-headers.ts @@ -0,0 +1,5 @@ +export const corsHeaders = { + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Headers": + "authorization, x-client-info, apikey, content-type", +}; diff --git a/supabase/functions/deno-packages/billing-functions/lib/create-supabase-client.ts b/supabase/functions/deno-packages/billing-functions/lib/create-supabase-client.ts new file mode 100644 index 0000000..379ae0b --- /dev/null +++ b/supabase/functions/deno-packages/billing-functions/lib/create-supabase-client.ts @@ -0,0 +1,13 @@ +import { createSupabaseClient } from "../deps.ts"; +import { Database } from "./types/supabase.ts"; + +export default function(authToken: string) { + const supabase = createSupabaseClient( + Deno.env.get("SUPABASE_URL") as string, + Deno.env.get("SUPABASE_ANON_KEY") as string, + { + global: { headers: { Authorization: authToken } }, + } + ); + return supabase; +} diff --git a/supabase/functions/deno-packages/billing-functions/lib/create-supabase-service-client.ts b/supabase/functions/deno-packages/billing-functions/lib/create-supabase-service-client.ts new file mode 100644 index 0000000..d9f239d --- /dev/null +++ b/supabase/functions/deno-packages/billing-functions/lib/create-supabase-service-client.ts @@ -0,0 +1,10 @@ +import {createSupabaseClient} from "../deps.ts"; +import {Database} from "./types/supabase.ts"; + +export default function () { + const supabase = createSupabaseClient( + Deno.env.get("SUPABASE_URL") as string, + Deno.env.get("SUPABASE_SERVICE_ROLE_KEY") as string + ); + return supabase; +} diff --git a/supabase/functions/deno-packages/billing-functions/lib/error-response.ts b/supabase/functions/deno-packages/billing-functions/lib/error-response.ts new file mode 100644 index 0000000..28531ac --- /dev/null +++ b/supabase/functions/deno-packages/billing-functions/lib/error-response.ts @@ -0,0 +1,16 @@ +import {corsHeaders} from "./cors-headers.ts"; + +export default function errorResponse(message: string, code: number = 400): Response { + return new Response( + JSON.stringify({ + error: message + }), + { + status: code, + headers: { + ...corsHeaders, + "Content-Type": "application/json" + } + } + ) +} \ No newline at end of file diff --git a/supabase/functions/deno-packages/billing-functions/lib/upsert-data.ts b/supabase/functions/deno-packages/billing-functions/lib/upsert-data.ts new file mode 100644 index 0000000..af42470 --- /dev/null +++ b/supabase/functions/deno-packages/billing-functions/lib/upsert-data.ts @@ -0,0 +1,55 @@ +import {Database as BASEJUMP_DATABASE_SCHEMA} from '../types/basejump-database.ts'; + +export type BASEJUMP_BILLING_DATA_UPSERT = { + provider: BASEJUMP_DATABASE_SCHEMA["public"]["Tables"]["billing_providers"]["Row"]["provider"]; + customer?: { + id: string; + billing_email?: string; + account_id: string; + provider: string; + }; + subscription?: { + id: string; + billing_customer_id?: string; + status: BASEJUMP_DATABASE_SCHEMA["public"]["Tables"]["billing_subscriptions"]["Row"]["status"]; + account_id: string; + created_at: Date; + updated_at: Date; + cancel_at?: Date; + cancel_at_period_end?: boolean; + canceled_at?: Date; + current_period_end?: Date; + current_period_start?: Date; + ended_at?: Date; + metadata?: { + [key: string]: any; + }; + price_id?: string; + quantity?: number; + trial_end?: Date; + trial_start?: Date; + plan_name?: string; + provider: string; + }; +}; + +export async function upsertCustomerSubscription( + supabaseClient, + accountId, + upsertData: BASEJUMP_BILLING_DATA_UPSERT +) { + const {data, error} = await supabaseClient.rpc( + "service_role_upsert_customer_subscription", + { + account_id: accountId, + customer: upsertData.customer, + subscription: upsertData.subscription, + } + ); + + if (error) { + throw error; + } + + return data; +} diff --git a/supabase/functions/deno-packages/billing-functions/lib/validate-url.ts b/supabase/functions/deno-packages/billing-functions/lib/validate-url.ts new file mode 100644 index 0000000..6f3a515 --- /dev/null +++ b/supabase/functions/deno-packages/billing-functions/lib/validate-url.ts @@ -0,0 +1,10 @@ +export default function validateUrl(url: string, allowedURLs: string[]) { + if (!url) { + return false; + } + const urlObject = new URL(url); + const allowedOriginObjects = allowedURLs.map(url => new URL(url)); + return allowedOriginObjects.some(allowedURLObject => { + return urlObject.origin === allowedURLObject.origin; + }); +} \ No newline at end of file diff --git a/supabase/functions/deno-packages/billing-functions/mod.ts b/supabase/functions/deno-packages/billing-functions/mod.ts new file mode 100644 index 0000000..130beb2 --- /dev/null +++ b/supabase/functions/deno-packages/billing-functions/mod.ts @@ -0,0 +1,17 @@ +/** + * Utility functions devs + */ +export { requireAuthorizedUser } from "./src/require-authorized-user.ts"; +export { requireAuthorizedBillingUser } from "./src/require-authorized-billing-user.ts"; + +/** + * Billing edge function wrappers + */ +export { billingFunctionsWrapper } from "./src/billing-functions-wrapper.ts"; +export { billingWebhooksWrapper } from "./src/billing-webhooks-wrapper.ts"; + +/** + * Stripe Handlers + */ +export { stripeFunctionHandler } from "./src/providers/stripe/stripe-function-handler.ts"; +export { stripeWebhookHandler } from "./src/providers/stripe/stripe-webhook-handler.ts"; diff --git a/supabase/functions/deno-packages/billing-functions/src/billing-functions-wrapper.ts b/supabase/functions/deno-packages/billing-functions/src/billing-functions-wrapper.ts new file mode 100644 index 0000000..0e60983 --- /dev/null +++ b/supabase/functions/deno-packages/billing-functions/src/billing-functions-wrapper.ts @@ -0,0 +1,207 @@ +import {Database as BASEJUMP_DATABASE_SCHEMA} from "../types/basejump-database.ts"; +import {requireAuthorizedBillingUser} from "./require-authorized-billing-user.ts"; +import getBillingStatus from "./wrappers/get-billing-status.ts"; +import createSupabaseServiceClient from "../lib/create-supabase-service-client.ts"; +import {corsHeaders} from "../lib/cors-headers.ts"; +import {BASEJUMP_BILLING_DATA_UPSERT} from "../lib/upsert-data.ts"; +import validateUrl from "../lib/validate-url.ts"; +import errorResponse from "../lib/error-response.ts"; + +type GET_PLANS_ARGS = { + account_id?: string; +}; + +type GET_PLANS_RESPONSE = Array<{ + id: string; + name: string; + description?: string; + amount: number; + currency: string; + interval: "month" | "year" | "one_time"; + interval_count: 1; + trial_period_days?: 30; + active?: boolean; + metadata?: { + [key: string]: string; + }; +}>; + +type GET_BILLING_PORTAL_URL_ARGS = { + accountId: string; + subscriptionId: string; + customerId?: string; + returnUrl: string; +}; + +type GET_NEW_SUBSCRIPTION_URL_ARGS = { + accountId: string; + planId: string; + successUrl: string; + cancelUrl: string; + billingEmail: string; + customerId?: string; +}; + +type GET_BILLING_STATUS_ARGS = { + accountId: string; + billingEmail?: string; + defaultPlanId?: string; + defaultTrialDays?: number; + customerId?: string; + subscriptionId?: string; +}; + +type GENERIC_URL_RESPONSE = { + url: string; +}; + +export type GET_BILLING_STATUS_RESPONSE = { + subscription_id: string; + subscription_active: boolean; + status: BASEJUMP_DATABASE_SCHEMA["public"]["Tables"]["billing_subscriptions"]["Row"]["status"]; + billing_email?: string; + account_role: BASEJUMP_DATABASE_SCHEMA["public"]["Tables"]["account_user"]["Row"]["account_role"]; + is_primary_owner: boolean; + billing_enabled: boolean; +}; + +type BILLING_FUNCTION_WRAPPER_OPTIONS = { + allowedURLs: Array; +} + +export type BILLING_FUNCTION_WRAPPER_HANDLERS = { + provider: BASEJUMP_DATABASE_SCHEMA["public"]["Tables"]["billing_providers"]["Row"]["provider"]; + getPlans: (args: GET_PLANS_ARGS) => Promise; + getBillingPortalUrl: ( + args: GET_BILLING_PORTAL_URL_ARGS + ) => Promise; + getNewSubscriptionUrl: ( + args: GET_NEW_SUBSCRIPTION_URL_ARGS + ) => Promise; + getBillingStatus: ( + args: GET_BILLING_STATUS_ARGS + ) => Promise; +}; + +export function billingFunctionsWrapper( + handlers: BILLING_FUNCTION_WRAPPER_HANDLERS, + options: BILLING_FUNCTION_WRAPPER_OPTIONS = { + allowedURLs: [], + } +): (req: Request) => Promise { + return async function (req: Request) { + // check for options preflight and handle cors + if (req.method === "OPTIONS") { + return new Response("ok", {headers: corsHeaders}); + } + const body = await req.json(); + + if (!body.args?.account_id) { + return errorResponse("Account id is required"); + } + try { + switch (body.action) { + case "get_plans": + const plans = await handlers.getPlans(body.args); + return new Response(JSON.stringify(plans), { + headers: { + ...corsHeaders, + "Content-Type": "application/json", + }, + }); + case "get_billing_portal_url": + if (!validateUrl(body.args.return_url, options.allowedURLs)) { + return errorResponse("Return url is not allowed"); + } + return await requireAuthorizedBillingUser(req, { + accountId: body.args.account_id, + authorizedRoles: ["owner"], + async onBillableAndAuthorized(roleInfo) { + const response = await handlers.getBillingPortalUrl({ + accountId: roleInfo.account_id, + subscriptionId: roleInfo.billing_subscription_id, + customerId: roleInfo.billing_customer_id, + returnUrl: body.args.return_url, + }); + return new Response( + JSON.stringify({ + billing_enabled: roleInfo.billing_enabled, + ...response, + }), + { + headers: { + ...corsHeaders, + "Content-Type": "application/json", + }, + } + ); + }, + }); + case "get_new_subscription_url": + if (!validateUrl(body.args.success_url, options.allowedURLs) || !validateUrl(body.args.cancel_url, options.allowedURLs)) { + return errorResponse("Success or cancel url is not allowed"); + } + return await requireAuthorizedBillingUser(req, { + accountId: body.args.account_id, + authorizedRoles: ["owner"], + async onBillableAndAuthorized(roleInfo) { + const response = await handlers.getNewSubscriptionUrl({ + accountId: roleInfo.account_id, + planId: body.args.plan_id, + successUrl: body.args.success_url, + cancelUrl: body.args.cancel_url, + billingEmail: roleInfo.billing_email, + customerId: roleInfo.billing_customer_id, + }); + return new Response( + JSON.stringify({ + billing_enabled: roleInfo.billing_enabled, + ...response, + }), + { + headers: { + ...corsHeaders, + "Content-Type": "application/json", + }, + } + ); + }, + }); + + case "get_billing_status": + return await requireAuthorizedBillingUser(req, { + accountId: body.args.account_id, + authorizedRoles: ["owner"], + async onBillableAndAuthorized(roleInfo) { + const supabaseClient = createSupabaseServiceClient(); + const response = await getBillingStatus( + supabaseClient, + roleInfo, + handlers + ); + + return new Response( + JSON.stringify({ + ...response, + status: response.status || "not_setup", + billing_enabled: roleInfo.billing_enabled, + }), + { + headers: { + ...corsHeaders, + "Content-Type": "application/json", + }, + } + ); + }, + }); + + default: + return errorResponse("Invalid action"); + } + } catch (e) { + console.log(e); + return errorResponse("Internal server error", 500); + } + }; +} diff --git a/supabase/functions/deno-packages/billing-functions/src/billing-webhooks-wrapper.ts b/supabase/functions/deno-packages/billing-functions/src/billing-webhooks-wrapper.ts new file mode 100644 index 0000000..fdf1519 --- /dev/null +++ b/supabase/functions/deno-packages/billing-functions/src/billing-webhooks-wrapper.ts @@ -0,0 +1,41 @@ +import createSupabaseServiceClient from "../lib/create-supabase-service-client.ts"; +import {BASEJUMP_BILLING_DATA_UPSERT, upsertCustomerSubscription,} from "../lib/upsert-data.ts"; + +export type BILLING_WEBHOOKS_WRAPPER_HANDLER = ( + req: Request +) => Promise; + +export function billingWebhooksWrapper( + handler: BILLING_WEBHOOKS_WRAPPER_HANDLER +): (req: Request) => Promise { + return async function (req: Request) { + try { + const data = await handler(req); + const accountId = + data?.customer?.account_id || data?.subscription?.account_id; + + if (data && accountId) { + const supabaseClient = createSupabaseServiceClient(); + // if we got data back from the webhook, save it + await upsertCustomerSubscription(supabaseClient, accountId, data); + } + + return new Response(JSON.stringify({message: "Webhook processed"}), { + status: 200, + headers: { + "Content-Type": "application/json", + }, + }); + } catch (e) { + return new Response( + JSON.stringify({error: "Error processing webhook"}), + { + status: 500, + headers: { + "Content-Type": "application/json", + }, + } + ); + } + }; +} diff --git a/supabase/functions/deno-packages/billing-functions/src/providers/stripe/billing-functions/find-or-create-customer.ts b/supabase/functions/deno-packages/billing-functions/src/providers/stripe/billing-functions/find-or-create-customer.ts new file mode 100644 index 0000000..d142ce9 --- /dev/null +++ b/supabase/functions/deno-packages/billing-functions/src/providers/stripe/billing-functions/find-or-create-customer.ts @@ -0,0 +1,54 @@ +import {stripeCustomerToBasejumpCustomer} from "./stripe-utils.ts"; +import {BASEJUMP_BILLING_DATA_UPSERT} from "../../../../lib/upsert-data.ts"; + +export async function findOrCreateCustomer( + stripeClient, + {customerId, billingEmail, accountId} +): Promise { + // if we have a customer ID, lookup the customer and return newest data + if (customerId) { + const customer = await stripeClient.customers.retrieve(customerId); + if (customer) { + return stripeCustomerToBasejumpCustomer(accountId, customer); + } + } + + // search for the customer using the billingEmail, then verify metadata account_id + if (billingEmail) { + const customer = await stripeClient.customers.list({ + email: billingEmail, + }); + if (customer.data.length > 0) { + const matchingCustomer = customer.data.find( + (customer) => customer.metadata?.basejump_account_id === accountId + ); + if (matchingCustomer) { + return stripeCustomerToBasejumpCustomer(accountId, matchingCustomer); + } + } + } + + // search stripe for a customer with accountId in the metadata + const customer = await stripeClient.customers.search({ + query: `metadata["basejump_account_id"]:"${accountId}"`, + limit: 1, + }); + + if (customer.data.length > 0) { + return stripeCustomerToBasejumpCustomer(accountId, customer.data[0]); + } + + const customerData: { metadata: { basejump_account_id: string }; email?: string } = { + metadata: { + basejump_account_id: accountId, + }, + }; + + if (billingEmail) { + customerData.email = billingEmail; + } + + const createdCustomer = await stripeClient.customers.create(customerData); + + return stripeCustomerToBasejumpCustomer(accountId, createdCustomer); +} diff --git a/supabase/functions/deno-packages/billing-functions/src/providers/stripe/billing-functions/find-or-create-subscription.ts b/supabase/functions/deno-packages/billing-functions/src/providers/stripe/billing-functions/find-or-create-subscription.ts new file mode 100644 index 0000000..616292f --- /dev/null +++ b/supabase/functions/deno-packages/billing-functions/src/providers/stripe/billing-functions/find-or-create-subscription.ts @@ -0,0 +1,66 @@ +import {Stripe} from "../../../../deps.ts"; +import {stripeSubscriptionToBasejumpSubscription} from "./stripe-utils.ts"; +import {BASEJUMP_BILLING_DATA_UPSERT} from "../../../../lib/upsert-data.ts"; + +export async function findOrCreateSubscription( + stripeClient: Stripe.Client, + {customerId, subscriptionId, accountId, defaultPlanId, defaultTrialDays} +): Promise { + if (!customerId) { + throw new Error("customerId is required"); + } + + // if we have the subscription ID, we can just return it + if (subscriptionId) { + const subscription = await stripeClient.subscriptions.retrieve( + subscriptionId + ); + if (subscription) { + return stripeSubscriptionToBasejumpSubscription(accountId, subscription); + } + } + + if (!customerId) { + throw new Error("customerId is required"); + } + + //If we don't have it, we can search for the metadata + const customerSubscriptions = await stripeClient.subscriptions.list({ + customer: customerId, + }); + + if (customerSubscriptions.data.length > 0) { + // check to see if we have any that are for this account + const subscription = customerSubscriptions.data.find( + (s) => s.metadata?.basejump_account_id === accountId + ); + if (subscription) { + return stripeSubscriptionToBasejumpSubscription(accountId, subscription); + } + } + + // nope, so we need to try and create it + if (!defaultPlanId) { + return; + } + + const price = await stripeClient.prices.retrieve(defaultPlanId); + + // if the price doesn't exist, or price is not free and there is no trial period, return + // this is because we can't create the subscription without a payment method + if (!price || (price.unit_amount > 0 && !defaultTrialDays)) { + return; + } + + const newSubscription = await stripeClient.subscriptions.create({ + customer: customerId, + items: [{price: defaultPlanId}], + expand: ["latest_invoice.payment_intent"], + trial_period_days: Number(defaultTrialDays), + metadata: { + basejump_account_id: accountId, + }, + }); + + return stripeSubscriptionToBasejumpSubscription(accountId, newSubscription); +} diff --git a/supabase/functions/deno-packages/billing-functions/src/providers/stripe/billing-functions/get-plans.ts b/supabase/functions/deno-packages/billing-functions/src/providers/stripe/billing-functions/get-plans.ts new file mode 100644 index 0000000..dba649a --- /dev/null +++ b/supabase/functions/deno-packages/billing-functions/src/providers/stripe/billing-functions/get-plans.ts @@ -0,0 +1,17 @@ +export default async function getPlans(stripeClient) { + const prices = await stripeClient.prices.list({ + expand: ["data.product"], + }); + + return prices?.data?.map((price: Stripe.Price) => { + return { + product_name: price.product.name, + product_description: price.product.description, + currency: price.currency, + price: price.unit_amount, + id: price.id, + interval: + price.type === "one_time" ? "one_time" : price.recurring?.interval, + }; + }); +} diff --git a/supabase/functions/deno-packages/billing-functions/src/providers/stripe/billing-functions/stripe-utils.ts b/supabase/functions/deno-packages/billing-functions/src/providers/stripe/billing-functions/stripe-utils.ts new file mode 100644 index 0000000..4843590 --- /dev/null +++ b/supabase/functions/deno-packages/billing-functions/src/providers/stripe/billing-functions/stripe-utils.ts @@ -0,0 +1,53 @@ +import { Stripe } from "../../../../deps.ts"; +import { BASEJUMP_BILLING_DATA_UPSERT } from "../../../../lib/upsert-data.ts"; + +function unixToIso(unixTime: number) { + return new Date(unixTime * 1000).toISOString(); +} + +export function stripeCustomerToBasejumpCustomer( + accountId: string, + stripeCustomer: Stripe.Customer +): BASEJUMP_BILLING_DATA_UPSERT["customer"] { + return { + id: stripeCustomer.id, + billing_email: stripeCustomer.email, + account_id: accountId, + provider: "stripe", + }; +} + +export function stripeSubscriptionToBasejumpSubscription( + accountId: string, + subscription: Stripe.Subscription +): BASEJUMP_BILLING_DATA_UPSERT["subscription"] { + return { + id: subscription.id, + account_id: accountId, + billing_customer_id: subscription.customer, + metadata: subscription.metadata, + status: subscription.status, + price_id: subscription.items.data[0].price.id, + quantity: subscription.items.data[0].quantity, + cancel_at_period_end: subscription.cancel_at_period_end, + cancel_at: subscription.cancel_at + ? unixToIso(subscription.cancel_at) + : null, + canceled_at: subscription.canceled_at + ? unixToIso(subscription.canceled_at) + : null, + current_period_start: new Date( + subscription.current_period_start + ).toISOString(), + current_period_end: unixToIso(subscription.current_period_end), + created: unixToIso(subscription.created), + ended_at: subscription.ended_at ? unixToIso(subscription.ended_at) : null, + trial_start: subscription.trial_start + ? unixToIso(subscription.trial_start) + : null, + trial_end: subscription.trial_end + ? unixToIso(subscription.trial_end) + : null, + provider: "stripe", + }; +} diff --git a/supabase/functions/deno-packages/billing-functions/src/providers/stripe/stripe-function-handler.ts b/supabase/functions/deno-packages/billing-functions/src/providers/stripe/stripe-function-handler.ts new file mode 100644 index 0000000..8c16f06 --- /dev/null +++ b/supabase/functions/deno-packages/billing-functions/src/providers/stripe/stripe-function-handler.ts @@ -0,0 +1,114 @@ +import {BILLING_FUNCTION_WRAPPER_HANDLERS} from "../../billing-functions-wrapper.ts"; +import getPlans from "./billing-functions/get-plans.ts"; +import {findOrCreateCustomer} from "./billing-functions/find-or-create-customer.ts"; +import {findOrCreateSubscription} from "./billing-functions/find-or-create-subscription.ts"; +import {Stripe} from "../../../deps.ts"; + +type Props = { + stripeClient: Stripe.Client; + defaultTrialDays?: number; + defaultPlanId?: string; +}; + +export function stripeFunctionHandler({ + stripeClient, + defaultTrialDays, + defaultPlanId + }: Props): BILLING_FUNCTION_WRAPPER_HANDLERS { + return { + provider: "stripe", + async getPlans() { + return getPlans(stripeClient); + }, + + async getBillingStatus({ + accountId, + customerId, + billingEmail, + defaultTrialDays, + defaultPlanId, + subscriptionId, + }) { + const customer = await findOrCreateCustomer(stripeClient, { + customerId, + billingEmail, + accountId, + }); + + const subscription = await findOrCreateSubscription(stripeClient, { + subscriptionId, + customerId: customer?.id, + defaultPlanId, + accountId, + defaultTrialDays, + }); + + return { + provider: 'stripe', + customer, + subscription, + }; + }, + async getNewSubscriptionUrl({ + successUrl, + cancelUrl, + accountId, + planId, + billingEmail, + customerId, + }) { + + const customer = await findOrCreateCustomer(stripeClient, { + customerId, + billingEmail, + accountId, + }); + + if (!customer) { + throw new Error("Customer not found"); + } + + const trialEnd = defaultTrialDays ? Math.floor(Date.now() / 1000) + (defaultTrialDays * 24 * 60 * 60) : undefined; + + const session = await stripeClient.checkout.sessions.create({ + customer: customer.id, + subscription_data: { + trial_end: trialEnd, + trial_settings: { + end_behavior: { + missing_payment_method: 'create_invoice' // subscription will go past_due if no payment method is added in time + } + }, + metadata: { + basejump_account_id: accountId, + }, + items: [ + { + plan: planId || defaultPlanId + }, + ], + }, + mode: "subscription", + success_url: successUrl, + cancel_url: cancelUrl, + metadata: { + basejump_account_id: accountId, + }, + }); + + return { + url: session.url, + }; + }, + async getBillingPortalUrl({returnUrl, customerId}) { + const session = await stripeClient.billingPortal.sessions.create({ + customer: customerId, + return_url: returnUrl, + }); + + return { + url: session.url, + }; + }, + }; +} diff --git a/supabase/functions/deno-packages/billing-functions/src/providers/stripe/stripe-webhook-handler.ts b/supabase/functions/deno-packages/billing-functions/src/providers/stripe/stripe-webhook-handler.ts new file mode 100644 index 0000000..402ec7c --- /dev/null +++ b/supabase/functions/deno-packages/billing-functions/src/providers/stripe/stripe-webhook-handler.ts @@ -0,0 +1,89 @@ +import {Stripe} from "../../../deps.ts"; +import { + stripeCustomerToBasejumpCustomer, + stripeSubscriptionToBasejumpSubscription +} from "./billing-functions/stripe-utils.ts"; +import {BASEJUMP_BILLING_DATA_UPSERT} from "../../../lib/upsert-data.ts"; + +const cryptoProvider = Stripe.createSubtleCryptoProvider(); + +const relevantEvents = new Set([ + "customer.subscription.created", + "customer.subscription.updated", + "customer.subscription.deleted", + "customer.created", + "customer.updated", + "customer.deleted", +]); + +type Props = { + stripeClient: Stripe.Client; + stripeWebhookSigningSecret: string; +}; + +export function stripeWebhookHandler({ + stripeClient, + stripeWebhookSigningSecret, + }: Props): (req: Request) => Promise { + return async (req) => { + const signature = req.headers.get("Stripe-Signature"); + const body = await req.text(); + const receivedEvent = await stripeClient.webhooks.constructEventAsync( + body, + signature!, + stripeWebhookSigningSecret, + undefined, + cryptoProvider + ); + + if (!relevantEvents.has(receivedEvent.type)) { + return; + } + + console.log('processing event', receivedEvent.type) + + switch (receivedEvent.type) { + case "customer.created": + case "customer.updated": + case "customer.deleted": { + const customerData = receivedEvent.data.object as Stripe.Customer; + const accountId = customerData.metadata.basejump_account_id; + if (!accountId) { + throw new Error( + "Customer created/updated/deleted event missing basejump_account_id" + ); + } + const customer = stripeCustomerToBasejumpCustomer( + accountId, + customerData + ); + + return {provider: 'stripe', customer}; + } + case "customer.subscription.created": + case "customer.subscription.updated": + case "customer.subscription.deleted": { + const subscriptionData = receivedEvent.data + .object as Stripe.Subscription; + const accountId = subscriptionData.metadata.basejump_account_id; + if (!accountId) { + throw new Error( + "Subscription created/updated/deleted event missing basejump_account_id" + ); + } + + const subscription = stripeSubscriptionToBasejumpSubscription( + accountId, + subscriptionData + ); + + return { + provider: "stripe", + subscription, + }; + } + default: + throw new Error("Unhandled relevant event!"); + } + }; +} diff --git a/supabase/functions/deno-packages/billing-functions/src/require-authorized-billing-user.ts b/supabase/functions/deno-packages/billing-functions/src/require-authorized-billing-user.ts new file mode 100644 index 0000000..17b6337 --- /dev/null +++ b/supabase/functions/deno-packages/billing-functions/src/require-authorized-billing-user.ts @@ -0,0 +1,93 @@ +import createSupabaseClient from "../lib/create-supabase-client.ts"; +import {BASEJUMP_DATABASE_SCHEMA} from "../mod.ts"; +import errorResponse from "../lib/error-response.ts"; + +export type AUTHORIZED_BILLING_USER_INFO = { + account_role: BASEJUMP_DATABASE_SCHEMA["public"]["Tables"]["account_user"]["Row"]["account_role"]; + is_primary_owner: boolean; + is_personal_account: boolean; + account_id: string; + billing_subscription_id: string; + billing_status: string; + billing_customer_id: string; + billing_email: string; + billing_enabled: boolean; + billing_provider?: string; +}; + +type REQUIRE_AUTHORIZED_BILLING_USER_OPTIONS = { + accountId: string; + authorizedRoles: string[]; + onBillingDisabled?: () => Promise; + onUnauthorized?: () => Promise; + onBillableAndAuthorized?: ( + roleInfo: AUTHORIZED_BILLING_USER_INFO + ) => Promise; + onError?: (e: Error) => Promise; +}; + +export async function requireAuthorizedBillingUser( + req: Request, + options: REQUIRE_AUTHORIZED_BILLING_USER_OPTIONS +): Promise { + try { + const authToken = req.headers.get("Authorization"); + const accountId = options.accountId; + // we don't have what we need. instant block. + if (!authToken || !accountId) { + if (options.onUnauthorized) { + return await options.onUnauthorized(); + } + return errorResponse("Unauthorized", 401); + } + + + const supabase = createSupabaseClient(authToken); + const {data, error} = await supabase.rpc("get_account_billing_status", { + account_id: options.accountId, + }); + + // means this user isn't a member of this account, block + if (!data || error) { + if (options.onUnauthorized) { + return await options.onUnauthorized(); + } + return errorResponse("Unauthorized", 401); + } + + // means this user is a member of this account, but not the right role, block + if (!options.authorizedRoles.includes(data.account_role)) { + if (options.onUnauthorized) { + return await options.onUnauthorized(); + } + return errorResponse("Unauthorized", 401); + } + + // means this user is a member of this account, but billing is disabled, just return a generic response + if (!data.billing_enabled) { + if (options.onBillingDisabled) { + return await options.onBillingDisabled(); + } + return new Response( + { + billing_enabled: false, + }, + { + headers: { + "Content-Type": "application/json", + }, + } + ); + } + + // means this user is a member of this account, and has the right role, allow + return await options.onBillableAndAuthorized(data); + } catch (e) { + // something went wrong, throw an error + if (options.onError) { + return options.onError(e); + } else { + return errorResponse("Internal Error", 500); + } + } +} diff --git a/supabase/functions/deno-packages/billing-functions/src/require-authorized-user.ts b/supabase/functions/deno-packages/billing-functions/src/require-authorized-user.ts new file mode 100644 index 0000000..f200e19 --- /dev/null +++ b/supabase/functions/deno-packages/billing-functions/src/require-authorized-user.ts @@ -0,0 +1,62 @@ +import { createSupabaseClient } from "../deps.ts"; +import { BASEJUMP_DATABASE_SCHEMA } from "../mod.ts"; + +type AUTHORIZED_USER_INFO = { + account_role: BASEJUMP_DATABASE_SCHEMA["public"]["Tables"]["account_user"]["Row"]["account_role"]; + is_primary_owner: boolean; + is_personal_account: boolean; +}; + +type REQUIRE_AUTHORIZED_USER_OPTIONS = { + accountId: string; + authorizedRoles: string[]; + onUnauthorized?: () => Promise; + onAuthorized: (roleInfo: AUTHORIZED_USER_INFO) => Promise; + onError?: (e: Error) => Promise; +}; + +export async function requireAuthorizedUser( + req: Request, + options: REQUIRE_AUTHORIZED_USER_OPTIONS +): Promise { + try { + const authToken = req.headers.get("Authorization"); + // we don't have what we need. instant block. + if (!authToken || !accountId) { + if (options.onUnauthorized) { + return await options.onUnauthorized(); + } + return new Response("Unauthorized", { status: 401 }); + } + + const supabase = createSupabaseClient(authToken); + const { data, error } = await supabase.rpc("current_user_account_role", { + account_id: options.accountId, + }); + // means this user isn't a member of this account, block + if (!data || error) { + if (options.onUnauthorized) { + return await options.onUnauthorized(); + } + return new Response("Unauthorized", { status: 401 }); + } + + // means this user is a member of this account, but not the right role, block + if (!options.authorizedRoles.includes(data.account_role)) { + if (options.onUnauthorized) { + return await options.onUnauthorized(); + } + return new Response("Unauthorized", { status: 401 }); + } + + // means this user is a member of this account, and has the right role, allow + return await options.onAuthorized(data); + } catch (e) { + // something went wrong, throw an error + if (options.onError) { + return options.onError(e); + } else { + return new Response("Internal Error", { status: 500 }); + } + } +} diff --git a/supabase/functions/deno-packages/billing-functions/src/wrappers/get-billing-status.ts b/supabase/functions/deno-packages/billing-functions/src/wrappers/get-billing-status.ts new file mode 100644 index 0000000..51af2c6 --- /dev/null +++ b/supabase/functions/deno-packages/billing-functions/src/wrappers/get-billing-status.ts @@ -0,0 +1,43 @@ +import {SupabaseClient} from "@supabase/supabase-js"; +import {AUTHORIZED_BILLING_USER_INFO} from "../require-authorized-billing-user.ts"; +import {BILLING_FUNCTION_WRAPPER_HANDLERS, GET_BILLING_STATUS_RESPONSE,} from "../billing-functions-wrapper.ts"; +import {upsertCustomerSubscription} from "../../lib/upsert-data.ts"; + +/** + * Responsible for handling the local data cache update for billing status + * from the given handler. + * @param supabaseClient + * @param roleInfo + * @param handlers + */ +export default async function getBillingStatus( + supabaseClient: SupabaseClient, + roleInfo: AUTHORIZED_BILLING_USER_INFO, + handlers: BILLING_FUNCTION_WRAPPER_HANDLERS +): Promise { + const billingData = await handlers.getBillingStatus({ + accountId: roleInfo.account_id, + billingEmail: roleInfo.billing_email, + customerId: roleInfo.billing_customer_id, + subscriptionId: roleInfo.billing_subscription_id, + }); + + const data = await upsertCustomerSubscription( + supabaseClient, + roleInfo.account_id, + billingData + ); + + return { + subscription_id: billingData?.subscription?.id, + plan_name: billingData?.subscription?.plan_name, + subscription_active: ["trialing", "active"].includes( + billingData?.subscription?.status + ), + status: billingData?.subscription?.status, + billing_email: billingData?.customer?.billing_email, + account_role: roleInfo.account_role, + is_primary_owner: roleInfo.is_primary_owner, + billing_enabled: roleInfo.billing_enabled, + }; +} diff --git a/supabase/functions/deno-packages/billing-functions/types/basejump-database.ts b/supabase/functions/deno-packages/billing-functions/types/basejump-database.ts new file mode 100644 index 0000000..0f1cca6 --- /dev/null +++ b/supabase/functions/deno-packages/billing-functions/types/basejump-database.ts @@ -0,0 +1,330 @@ +export type Json = + | string + | number + | boolean + | null + | { [key: string]: Json } + | Json[] + +export interface Database { + graphql_public: { + Tables: { + [_ in never]: never + } + Views: { + [_ in never]: never + } + Functions: { + graphql: { + Args: { + operationName?: string + query?: string + variables?: Json + extensions?: Json + } + Returns: Json + } + } + Enums: { + [_ in never]: never + } + CompositeTypes: { + [_ in never]: never + } + } + public: { + Tables: { + [_ in never]: never + } + Views: { + [_ in never]: never + } + Functions: { + accept_invitation: { + Args: { + lookup_invitation_token: string + } + Returns: string + } + create_account: { + Args: { + slug?: string + name?: string + } + Returns: Json + } + current_user_account_role: { + Args: { + lookup_account_id: string + } + Returns: Json + } + get_account: { + Args: { + account_id: string + } + Returns: Json + } + get_account_billing_status: { + Args: { + lookup_account_id: string + } + Returns: Json + } + get_account_by_slug: { + Args: { + slug: string + } + Returns: Json + } + get_accounts: { + Args: Record + Returns: Json + } + get_profile: { + Args: { + user_id?: string + } + Returns: Json + } + get_service_role_config: { + Args: Record + Returns: Json + } + lookup_invitation: { + Args: { + lookup_invitation_token: string + } + Returns: Json + } + service_role_upsert_customer_subscription: { + Args: { + account_id?: string + customer?: Json + subscription?: Json + } + Returns: undefined + } + update_account: { + Args: { + account_id: string + slug?: string + name?: string + public_metadata?: Json + replace_metadata?: boolean + } + Returns: Json + } + update_account_user_role: { + Args: { + account_id: string + user_id: string + new_account_role: "owner" | "member" + make_primary_owner: boolean + } + Returns: undefined + } + update_profile: { + Args: { + user_id?: string + name?: string + public_metadata?: Json + replace_metadata?: boolean + } + Returns: Json + } + } + Enums: { + [_ in never]: never + } + CompositeTypes: { + [_ in never]: never + } + } + storage: { + Tables: { + buckets: { + Row: { + allowed_mime_types: string[] | null + avif_autodetection: boolean | null + created_at: string | null + file_size_limit: number | null + id: string + name: string + owner: string | null + public: boolean | null + updated_at: string | null + } + Insert: { + allowed_mime_types?: string[] | null + avif_autodetection?: boolean | null + created_at?: string | null + file_size_limit?: number | null + id: string + name: string + owner?: string | null + public?: boolean | null + updated_at?: string | null + } + Update: { + allowed_mime_types?: string[] | null + avif_autodetection?: boolean | null + created_at?: string | null + file_size_limit?: number | null + id?: string + name?: string + owner?: string | null + public?: boolean | null + updated_at?: string | null + } + Relationships: [ + { + foreignKeyName: "buckets_owner_fkey" + columns: ["owner"] + referencedRelation: "users" + referencedColumns: ["id"] + } + ] + } + migrations: { + Row: { + executed_at: string | null + hash: string + id: number + name: string + } + Insert: { + executed_at?: string | null + hash: string + id: number + name: string + } + Update: { + executed_at?: string | null + hash?: string + id?: number + name?: string + } + Relationships: [] + } + objects: { + Row: { + bucket_id: string | null + created_at: string | null + id: string + last_accessed_at: string | null + metadata: Json | null + name: string | null + owner: string | null + path_tokens: string[] | null + updated_at: string | null + version: string | null + } + Insert: { + bucket_id?: string | null + created_at?: string | null + id?: string + last_accessed_at?: string | null + metadata?: Json | null + name?: string | null + owner?: string | null + path_tokens?: string[] | null + updated_at?: string | null + version?: string | null + } + Update: { + bucket_id?: string | null + created_at?: string | null + id?: string + last_accessed_at?: string | null + metadata?: Json | null + name?: string | null + owner?: string | null + path_tokens?: string[] | null + updated_at?: string | null + version?: string | null + } + Relationships: [ + { + foreignKeyName: "objects_bucketId_fkey" + columns: ["bucket_id"] + referencedRelation: "buckets" + referencedColumns: ["id"] + }, + { + foreignKeyName: "objects_owner_fkey" + columns: ["owner"] + referencedRelation: "users" + referencedColumns: ["id"] + } + ] + } + } + Views: { + [_ in never]: never + } + Functions: { + can_insert_object: { + Args: { + bucketid: string + name: string + owner: string + metadata: Json + } + Returns: undefined + } + extension: { + Args: { + name: string + } + Returns: string + } + filename: { + Args: { + name: string + } + Returns: string + } + foldername: { + Args: { + name: string + } + Returns: unknown + } + get_size_by_bucket: { + Args: Record + Returns: { + size: number + bucket_id: string + }[] + } + search: { + Args: { + prefix: string + bucketname: string + limits?: number + levels?: number + offsets?: number + search?: string + sortcolumn?: string + sortorder?: string + } + Returns: { + name: string + id: string + updated_at: string + created_at: string + last_accessed_at: string + metadata: Json + }[] + } + } + Enums: { + [_ in never]: never + } + CompositeTypes: { + [_ in never]: never + } + } +} + diff --git a/supabase/functions/test-stripe-billing-functions/index.ts b/supabase/functions/test-stripe-billing-functions/index.ts new file mode 100644 index 0000000..331df82 --- /dev/null +++ b/supabase/functions/test-stripe-billing-functions/index.ts @@ -0,0 +1,31 @@ +/*** + * THESE FUNCTIONS ARE FOR TESTING PURPOSES ONLY. + * TO SETUP ON YOUR OWN, HEAD TO https://usebasejump.com + */ +import {serve} from "https://deno.land/std@0.168.0/http/server.ts"; +import {billingFunctionsWrapper, stripeFunctionHandler} from "../deno-packages/billing-functions/mod.ts"; + +import Stripe from "https://esm.sh/stripe@11.1.0?target=deno"; + +const stripeClient = new Stripe(Deno.env.get("STRIPE_API_KEY") as string, { + // This is needed to use the Fetch API rather than relying on the Node http + // package. + apiVersion: "2022-11-15", + httpClient: Stripe.createFetchHttpClient(), +}); + +const stripeHandler = stripeFunctionHandler({ + stripeClient, + defaultPlanId: Deno.env.get("STRIPE_DEFAULT_PLAN_ID") as string, + defaultTrialDays: Deno.env.get("STRIPE_DEFAULT_TRIAL_DAYS") ? Number(Deno.env.get("STRIPE_DEFAULT_TRIAL_DAYS")) : undefined +}); + +const billingEndpoint = billingFunctionsWrapper(stripeHandler, { + allowedURLs: ['http://127.0.0.1:54323'] +}); + +serve(async (req) => { + const response = await billingEndpoint(req); + + return response; +}); diff --git a/supabase/functions/test-stripe-billing-webhooks/index.ts b/supabase/functions/test-stripe-billing-webhooks/index.ts new file mode 100644 index 0000000..fab197b --- /dev/null +++ b/supabase/functions/test-stripe-billing-webhooks/index.ts @@ -0,0 +1,29 @@ +/*** + * THESE FUNCTIONS ARE FOR TESTING PURPOSES ONLY. + * TO SETUP ON YOUR OWN, HEAD TO https://usebasejump.com + */ + +import {serve} from "https://deno.land/std@0.168.0/http/server.ts"; +import {billingWebhooksWrapper, stripeWebhookHandler} from "../deno-packages/billing-functions/mod.ts"; + + +import Stripe from "https://esm.sh/stripe@11.1.0?target=deno"; + +const stripeClient = new Stripe(Deno.env.get("STRIPE_API_KEY") as string, { + // This is needed to use the Fetch API rather than relying on the Node http + // package. + apiVersion: "2022-11-15", + httpClient: Stripe.createFetchHttpClient(), +}); + +const stripeResponse = stripeWebhookHandler({ + stripeClient, + stripeWebhookSigningSecret: Deno.env.get("STRIPE_WEBHOOK_SIGNING_SECRET") as string, +}); + +const webhookEndpoint = billingWebhooksWrapper(stripeResponse); + +serve(async (req) => { + const response = await webhookEndpoint(req); + return response; +}); diff --git a/supabase/migrations/00000000000000_dbdev_temp_install.sql b/supabase/migrations/00000000000000_dbdev_temp_install.sql deleted file mode 100644 index feae2c5..0000000 --- a/supabase/migrations/00000000000000_dbdev_temp_install.sql +++ /dev/null @@ -1,37 +0,0 @@ -/** - This should be automatically installed, but handling here manually until it is - https://database.dev/installer - */ -create extension if not exists http with schema extensions; -create extension if not exists pg_tle; -select pgtle.uninstall_extension_if_exists('supabase-dbdev'); -drop extension if exists "supabase-dbdev"; -select pgtle.install_extension( - 'supabase-dbdev', - resp.contents ->> 'version', - 'PostgreSQL package manager', - resp.contents ->> 'sql' - ) -from http( - ( - 'GET', - 'https://api.database.dev/rest/v1/' - || 'package_versions?select=sql,version' - || '&package_name=eq.supabase-dbdev' - || '&order=version.desc' - || '&limit=1', - array [ - ('apiKey', - 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InhtdXB0cHBsZnZpaWZyYndtbXR2Iiwicm9sZSI6ImFub24iLCJpYXQiOjE2ODAxMDczNzIsImV4cCI6MTk5NTY4MzM3Mn0.z2CN0mvO2No8wSi46Gw59DFGCTJrzM0AQKsu_5k134s')::http_header - ], - null, - null - ) - ) x, - lateral ( - select ((row_to_json(x) -> 'content') #>> '{}')::json -> 0 - ) resp(contents); -create extension "supabase-dbdev"; -select dbdev.install('supabase-dbdev'); -drop extension if exists "supabase-dbdev"; -create extension "supabase-dbdev"; \ No newline at end of file diff --git a/supabase/migrations/00000000000001_utility_functions.sql b/supabase/migrations/00000000000001_utility_functions.sql deleted file mode 100644 index a183c56..0000000 --- a/supabase/migrations/00000000000001_utility_functions.sql +++ /dev/null @@ -1,132 +0,0 @@ -/** - * We want to enable pgtap for testing - */ -create extension if not exists pgtap with schema extensions; -select dbdev.install('basejump-supabase_test_helpers'); - -/** - * By default we want to revoke execute from public - */ -ALTER DEFAULT PRIVILEGES REVOKE EXECUTE ON FUNCTIONS FROM PUBLIC; -ALTER DEFAULT PRIVILEGES IN SCHEMA PUBLIC REVOKE EXECUTE ON FUNCTIONS FROM anon, authenticated; - -/** - Create a schema for us to use for private functions - */ -CREATE SCHEMA IF NOT EXISTS basejump; -GRANT USAGE ON SCHEMA basejump to authenticated; -GRANT USAGE ON SCHEMA basejump to service_role; - -CREATE TABLE IF NOT EXISTS basejump.config -( - enable_personal_accounts boolean default true, - enable_team_accounts boolean default true -); - --- enable select on the config table -GRANT SELECT ON basejump.config TO authenticated, service_role; - --- enable RLS on config -ALTER TABLE basejump.config - ENABLE ROW LEVEL SECURITY; - -create policy "Basejump settings can be read by authenticated users" on basejump.config - for select - to authenticated - using ( - true - ); - -/** - Get the full config object to check basejump settings - This is not accessible fromt he outside, so can only be used inside postgres functions - */ -CREATE OR REPLACE FUNCTION basejump.get_config() - RETURNS json AS -$$ -DECLARE - result RECORD; -BEGIN - SELECT * from basejump.config limit 1 into result; - return row_to_json(result); -END; -$$ LANGUAGE plpgsql; - -grant execute on function basejump.get_config() to authenticated; - -/** - Sometimes it's useful for supabase admin clients to access settings - but we dont' want to expose this to anyone else, so it's not granted to anyone but - the service key - */ -CREATE OR REPLACE FUNCTION public.get_service_role_config() - RETURNS json AS -$$ -DECLARE - result RECORD; -BEGIN - SELECT * from basejump.config limit 1 into result; - return row_to_json(result); -END; -$$ LANGUAGE plpgsql; - -/** - Check a specific boolean config value - */ -CREATE OR REPLACE FUNCTION basejump.is_set(field_name text) - RETURNS boolean AS -$$ -DECLARE - result BOOLEAN; -BEGIN - execute format('select %I from basejump.config limit 1', field_name) into result; - return result; -END; -$$ LANGUAGE plpgsql; - -grant execute on function basejump.is_set(text) to authenticated; - - -/** - * Automatic handling for maintaining created_at and updated_at timestamps - * on tables - */ -CREATE OR REPLACE FUNCTION basejump.trigger_set_timestamps() - RETURNS TRIGGER AS -$$ -BEGIN - if TG_OP = 'INSERT' then - NEW.created_at = now(); - NEW.updated_at = now(); - else - NEW.updated_at = now(); - NEW.created_at = OLD.created_at; - end if; - RETURN NEW; -END -$$ LANGUAGE plpgsql; - -/** - Generates a secure token - used internally for invitation tokens - but could be used elsewhere. Check out the invitations table for more info on - how it's used - */ -CREATE OR REPLACE FUNCTION basejump.generate_token(length int) - RETURNS bytea AS -$$ -BEGIN - return replace(replace(replace(encode(gen_random_bytes(length)::bytea, 'base64'), '/', '-'), '+', '_'), '\', '-'); -END -$$ LANGUAGE plpgsql; - -grant execute on function basejump.generate_token(int) to authenticated; - --- TODO: is this needed? --- CREATE OR REPLACE FUNCTION trigger_id_protection() --- RETURNS TRIGGER AS --- $$ --- BEGIN --- NEW.id = OLD.id; --- RETURN NEW; --- END --- $$ LANGUAGE plpgsql; \ No newline at end of file diff --git a/supabase/migrations/00000000000002_accounts.sql b/supabase/migrations/00000000000002_accounts.sql deleted file mode 100644 index b62b48e..0000000 --- a/supabase/migrations/00000000000002_accounts.sql +++ /dev/null @@ -1,293 +0,0 @@ -/** - * Account roles allow you to provide permission levels to users - * when they're acting on an account. By default, we provide - * "owner" and "member". The only distinction is that owners can - * also manage billing and invite/remove account members. - */ -DROP TYPE IF EXISTS public.account_role; - -CREATE TYPE public.account_role AS ENUM ('owner', 'member'); - -/** - * Accounts are the primary grouping for most objects within - * the system. They have many users, and all billing is connected to - * an account. - */ - CREATE TABLE IF NOT EXISTS public.accounts - ( - id uuid unique NOT NULL DEFAULT uuid_generate_v4(), - -- defaults to the user who creates the account - -- this user cannot be removed from an account without changing - -- the primary owner first - primary_owner_user_id uuid references auth.users not null default auth.uid(), - -- Account name - team_name text, - personal_account boolean default false not null, - updated_at timestamp with time zone, - created_at timestamp with time zone, - PRIMARY KEY (id) - ); - -/** - * We want to protect some fields on accounts from being updated - * Specifically the primary owner user id and account id. - * primary_owner_user_id should be updated using the dedicated function - */ - CREATE OR REPLACE FUNCTION public.protect_account_fields() - RETURNS TRIGGER AS - $$ - BEGIN - - - IF current_user IN ('authenticated', 'anon') THEN - -- these are protected fields that users are not allowed to update themselves - -- platform admins should be VERY careful about updating them as well. - if NEW.id <> OLD.id - OR NEW.personal_account <> OLD.personal_account - OR NEW.primary_owner_user_id <> OLD.primary_owner_user_id - THEN - RAISE EXCEPTION 'You do not have permission to update this field'; - end if; - end if; - - RETURN NEW; - END - $$ LANGUAGE plpgsql; - -CREATE TRIGGER protect_account_fields - BEFORE UPDATE - ON public.accounts - FOR EACH ROW - EXECUTE FUNCTION public.protect_account_fields(); - --- enable RLS for accounts -alter table accounts - enable row level security; - --- protect the timestamps -CREATE TRIGGER set_accounts_timestamp - BEFORE INSERT OR UPDATE ON public.accounts - FOR EACH ROW - EXECUTE PROCEDURE basejump.trigger_set_timestamps(); - -/** - * Account users are the users that are associated with an account. - * They can be invited to join the account, and can have different roles. - * The system does not enforce any permissions for roles, other than restricting - * billing and account membership to only owners - */ -create table account_user ( - -- id of the user in the account - user_id uuid references auth.users not null, - -- id of the account the user is in - account_id uuid references accounts not null, - -- role of the user in the account - account_role account_role not null, - constraint account_user_pkey primary key(user_id, account_id) -); - --- enable RLS for account_user -alter table account_user - enable row level security; - -/** - * When an account gets created, we want to insert the current user as the first - * owner - */ -create function basejump.add_current_user_to_new_account() - returns trigger - language plpgsql -security definer -set search_path=public -as $$ - begin - if new.primary_owner_user_id = auth.uid() then - insert into public.account_user (account_id, user_id, account_role) - values (NEW.id, auth.uid(), 'owner'); - end if; - return NEW; - end; -$$; - --- trigger the function whenever a new account is created -CREATE TRIGGER add_current_user_to_new_account - AFTER INSERT - ON public.accounts - FOR EACH ROW - EXECUTE FUNCTION basejump.add_current_user_to_new_account(); - -/** - * Auth convenience functions - */ - - /** - * Returns the current user's role within a given account_id - * Exists in the public name space because it's accessible via the API - */ -create or replace function public.current_user_account_role(lookup_account_id uuid) -returns jsonb -language plpgsql -as $$ - declare - user_account_role account_role; - is_account_primary_owner boolean; - is_personal_account boolean; - begin - if lookup_account_id is null then - -- return an error - raise exception 'account_id is required'; - end if; - select account_role into user_account_role from public.account_user where user_id = auth.uid() and account_user.account_id = lookup_account_id; - select primary_owner_user_id = auth.uid(), personal_account into is_account_primary_owner, is_personal_account from public.accounts where id = lookup_account_id; - - if user_account_role is null then - return null; - end if; - - return jsonb_build_object( - 'account_role', user_account_role, - 'is_primary_owner', is_account_primary_owner, - 'is_personal_account', is_personal_account - ); - end; -$$; - -grant execute on function public.current_user_account_role(uuid) to authenticated; - -/** - * Let's you update a users role within an account if you are an owner of that account - **/ -create or replace function public.update_account_user_role(account_id uuid, user_id uuid, new_account_role account_role, make_primary_owner boolean) -returns void -security definer -set search_path=public -language plpgsql -as $$ - declare - is_account_owner boolean; - is_account_primary_owner boolean; - changing_primary_owner boolean; - begin - -- check if the user is an owner, and if they are, allow them to update the role - select (update_account_user_role.account_id IN ( SELECT basejump.get_accounts_for_current_user('owner') AS get_accounts_for_current_user)) into is_account_owner; - - if not is_account_owner then - raise exception 'You must be an owner of the account to update a users role'; - end if; - - -- check if the user being changed is the primary owner, if so its not allowed - select primary_owner_user_id = auth.uid(), primary_owner_user_id = update_account_user_role.user_id into is_account_primary_owner, changing_primary_owner from public.accounts where id = update_account_user_role.account_id; - - if changing_primary_owner = true and is_account_primary_owner = false then - raise exception 'You must be the primary owner of the account to change the primary owner'; - end if; - - update public.account_user set account_role = new_account_role where account_user.account_id = update_account_user_role.account_id and account_user.user_id = update_account_user_role.user_id; - - if make_primary_owner = true then - -- first we see if the current user is the owner, only they can do this - if is_account_primary_owner = false then - raise exception 'You must be the primary owner of the account to change the primary owner'; - end if; - - update public.accounts set primary_owner_user_id = update_account_user_role.user_id where id = update_account_user_role.account_id; - end if; - end; -$$; - -grant execute on function public.update_account_user_role(uuid, uuid, account_role, boolean) to authenticated; - -/** - * Returns account_ids that the current user is a member of. If you pass in a role, - * it'll only return accounts that the user is a member of with that role. - */ -create or replace function basejump.get_accounts_for_current_user(passed_in_role account_role default null) -returns setof uuid -language sql -security definer -set search_path=public -as $$ - select account_id - from public.account_user wu - where wu.user_id = auth.uid() - and - ( - wu.account_role = passed_in_role - or passed_in_role is null - ); -$$; - -grant execute on function basejump.get_accounts_for_current_user(account_role) to authenticated; - - -/** - * Account user permission policies - * Account viewers can all view other account members and their roles - */ -create policy "users can view their own account_users" on account_user - for select - to authenticated - using ( - user_id = auth.uid() - ); - -create policy "users can view their teammates" on account_user - for select - to authenticated - using ( - (account_id IN ( SELECT basejump.get_accounts_for_current_user() AS get_accounts_for_authenticated_user)) - ); - -/** - * Account members can be removed by owners. You cannot remove the primary account owner - */ -create policy "Account users can be deleted except for the primary account owner" on account_user - for delete -to authenticated - using ( - (account_id IN ( SELECT basejump.get_accounts_for_current_user('owner') AS get_accounts_for_current_user)) - AND - user_id != (select primary_owner_user_id from public.accounts where account_id = accounts.id) - ); - -/** - * Accounts are viewable by their owners and members - */ -create policy "Accounts are viewable by members" on accounts - for select - to authenticated - using ( - id in ( - select basejump.get_accounts_for_current_user() - ) - ); - -/** - * Accounts need to be readable by primary_owner_user_id so that the select - * after initial create is readable - */ -create policy "Accounts are viewable by primary owner" on accounts - for select - to authenticated - using ( - primary_owner_user_id = auth.uid() - ); - -/** - * Accounts can be created by any user - */ -create policy "Team accounts can be created by any user" on accounts - for insert - to authenticated - with check ( - basejump.is_set('enable_team_accounts') = true - and personal_account = false - ); - - -create policy "Accounts can be edited by owners" on accounts - for update - to authenticated - using ( - (id IN ( SELECT basejump.get_accounts_for_current_user('owner') AS get_accounts_for_current_user)) - ); diff --git a/supabase/migrations/00000000000003_billing_setup.sql b/supabase/migrations/00000000000003_billing_setup.sql deleted file mode 100644 index eae8988..0000000 --- a/supabase/migrations/00000000000003_billing_setup.sql +++ /dev/null @@ -1,200 +0,0 @@ -create type billing_providers as enum ('stripe'); - -/** - * CUSTOMERS - * Note: this is a private table that contains a mapping of user IDs to Strip customer IDs. - */ -create table billing_customers -( - -- UUID from auth.users - account_id uuid references accounts not null primary key, - -- The user's customer ID in Stripe. User must not be able to update this. - customer_id text, - -- The email address the customer wants to use for invoicing - email text, - -- The active status of a customer - active boolean, - -- The billing provider the customer is using - provider billing_providers -); - -alter table - billing_customers - enable row level security; - -create policy "Can only view own customer data." on billing_customers for - select - using (account_id IN - (SELECT basejump.get_accounts_for_current_user() AS get_accounts_for_current_user)); - --- No policies as this is a private table that the user must not have access to. -/** - * PRODUCTS - * Note: products are created and managed in Stripe and synced to our DB via Stripe webhooks. - */ -create table billing_products -( - -- Product ID from Stripe, e.g. prod_1234. - id text primary key, - -- Whether the product is currently available for purchase. - active boolean, - -- The product's name, meant to be displayable to the customer. Whenever this product is sold via a subscription, name will show up on associated invoice line item descriptions. - name text, - -- The product's description, meant to be displayable to the customer. Use this field to optionally store a long form explanation of the product being sold for your own rendering purposes. - description text, - -- A URL of the product image in Stripe, meant to be displayable to the customer. - image text, - -- Set of key-value pairs, used to store additional information about the object in a structured format. - metadata jsonb, - provider billing_providers -); - -alter table - billing_products - enable row level security; - -create policy "Allow public read-only access." on billing_products for - select - using (true); - -/** - * PRICES - * Note: prices are created and managed in Stripe and synced to our DB via Stripe webhooks. - */ -create type pricing_type as enum ('one_time', 'recurring'); - -create type pricing_plan_interval as enum ('day', 'week', 'month', 'year'); - -create table billing_prices -( - -- Price ID from Stripe, e.g. price_1234. - id text primary key, - -- The ID of the prduct that this price belongs to. - billing_product_id text references billing_products, - -- Whether the price can be used for new purchases. - active boolean, - -- A brief description of the price. - description text, - -- The unit amount as a positive integer in the smallest currency unit (e.g., 100 cents for US$1.00 or 100 for ¥100, a zero-decimal currency). - unit_amount bigint, - -- Three-letter ISO currency code, in lowercase. - currency text check (char_length(currency) = 3), - -- One of `one_time` or `recurring` depending on whether the price is for a one-time purchase or a recurring (subscription) purchase. - type pricing_type, - -- The frequency at which a subscription is billed. One of `day`, `week`, `month` or `year`. - interval pricing_plan_interval, - -- The number of intervals (specified in the `interval` attribute) between subscription billings. For example, `interval=month` and `interval_count=3` bills every 3 months. - interval_count integer, - -- Default number of trial days when subscribing a customer to this price using [`trial_from_plan=true`](https://stripe.com/docs/api#create_subscription-trial_from_plan). - trial_period_days integer, - -- Set of key-value pairs, used to store additional information about the object in a structured format. - metadata jsonb, - provider billing_providers -); - -alter table - billing_prices - enable row level security; - -create policy "Allow public read-only access." on billing_prices for - select - using (true); - -/** - * SUBSCRIPTIONS - * Note: subscriptions are created and managed in Stripe and synced to our DB via Stripe webhooks. - */ -create type subscription_status as enum ( - 'trialing', - 'active', - 'canceled', - 'incomplete', - 'incomplete_expired', - 'past_due', - 'unpaid' - ); - -create table billing_subscriptions -( - -- Subscription ID from Stripe, e.g. sub_1234. - id text primary key, - account_id uuid references accounts not null, - -- The status of the subscription object, one of subscription_status type above. - status subscription_status, - -- Set of key-value pairs, used to store additional information about the object in a structured format. - metadata jsonb, - -- ID of the price that created this subscription. - price_id text references billing_prices, - -- Quantity multiplied by the unit amount of the price creates the amount of the subscription. Can be used to charge multiple seats. - quantity integer, - -- If true the subscription has been canceled by the user and will be deleted at the end of the billing period. - cancel_at_period_end boolean, - -- Time at which the subscription was created. - created timestamp with time zone default timezone('utc' :: text, now()) not null, - -- Start of the current period that the subscription has been invoiced for. - current_period_start timestamp with time zone default timezone('utc' :: text, now()) not null, - -- End of the current period that the subscription has been invoiced for. At the end of this period, a new invoice will be created. - current_period_end timestamp with time zone default timezone('utc' :: text, now()) not null, - -- If the subscription has ended, the timestamp of the date the subscription ended. - ended_at timestamp with time zone default timezone('utc' :: text, now()), - -- A date in the future at which the subscription will automatically get canceled. - cancel_at timestamp with time zone default timezone('utc' :: text, now()), - -- If the subscription has been canceled, the date of that cancellation. If the subscription was canceled with `cancel_at_period_end`, `canceled_at` will still reflect the date of the initial cancellation request, not the end of the subscription period when the subscription is automatically moved to a canceled state. - canceled_at timestamp with time zone default timezone('utc' :: text, now()), - -- If the subscription has a trial, the beginning of that trial. - trial_start timestamp with time zone default timezone('utc' :: text, now()), - -- If the subscription has a trial, the end of that trial. - trial_end timestamp with time zone default timezone('utc' :: text, now()), - provider billing_providers -); - -alter table - billing_subscriptions - enable row level security; - -create policy "Can only view own subs data." on billing_subscriptions for - select - using (account_id IN - (SELECT basejump.get_accounts_for_current_user() AS get_accounts_for_current_user)); - - -CREATE OR REPLACE FUNCTION public.get_account_billing_status(lookup_account_id uuid) - RETURNS json AS -$$ -DECLARE - result RECORD; -BEGIN - select s.id, - s.status, - c.email as billing_email, - p.name as plan_name - from billing_subscriptions s - join billing_prices pr on pr.id = s.price_id - join billing_products p on p.id = pr.billing_product_id - join billing_customers c on c.account_id = s.account_id - where s.account_id = lookup_account_id - order by s.created desc - limit 1 - into result; - - if result is null then - raise 'No billing data found for account %', lookup_account_id; - end if; - - return row_to_json(result); -END; -$$ LANGUAGE plpgsql; - -grant execute on function public.get_account_billing_status(uuid) to authenticated, service_role; - -/** - Add config options to basejump.config to setup the stripe requirements on new accounts - */ -alter table basejump.config - add column enable_account_billing boolean not null default true; -alter table basejump.config - add column billing_provider billing_providers default 'stripe'; -alter table basejump.config - add column stripe_default_trial_period_days integer default 30; -alter table basejump.config - add column stripe_default_account_price_id text references billing_prices; \ No newline at end of file diff --git a/supabase/migrations/00000000000010_profiles.sql b/supabase/migrations/00000000000010_profiles.sql deleted file mode 100644 index bdece31..0000000 --- a/supabase/migrations/00000000000010_profiles.sql +++ /dev/null @@ -1,115 +0,0 @@ -/** - * Creating a profile table is a recommended convention for Supabase - * Any data related to the user can be added here instead of directly - * on your auth.user table. The email is added here only for information purposes - * it's needed to let account members know who's an active member - * You cannot edit the email directly in the profile, you must change - * the email of the user using the provided Supabase methods - */ -create table public.profiles -( - -- the user's ID from the auth.users table out of supabase - id uuid unique references auth.users not null, - -- the user's name - name text, - -- when the profile was created - updated_at timestamp with time zone, - -- when the profile was last updated - created_at timestamp with time zone, - primary key (id) -); - --- Create the relationship with auth.users so we can do a join query --- using postgREST -ALTER TABLE public.account_user - ADD CONSTRAINT account_user_profiles_fkey FOREIGN KEY (user_id) - REFERENCES profiles (id) MATCH SIMPLE - ON UPDATE NO ACTION - ON DELETE NO ACTION; - --- manage timestamps -CREATE TRIGGER set_profiles_timestamp - BEFORE INSERT OR UPDATE - ON public.profiles - FOR EACH ROW -EXECUTE FUNCTION basejump.trigger_set_timestamps(); - - -alter table public.profiles - enable row level security; - --- permissions for viewing profiles for user and team members (ideally as two separate policies) --- add permissions for updating profiles for the user only -create policy "Users can view their own profiles" on profiles - for select - to authenticated - using ( - id = auth.uid() - ); - -create policy "Users can view their teammates profiles" on profiles - for select - to authenticated - using ( - id IN (SELECT account_user.user_id - FROM account_user - WHERE (account_user.user_id <> auth.uid())) - ); - - -create policy "Profiles are editable by their own user only" on profiles - for update - to authenticated - using ( - id = auth.uid() - ); - -/** - * We maintain a profile table with users information. - * We also want to provide an option to automatically create the first account - * for a new user. This is a good way to get folks through the onboarding flow easier - * potentially - */ -create function basejump.run_new_user_setup() - returns trigger - language plpgsql - security definer - set search_path = public -as -$$ -declare - first_account_name text; - first_account_id uuid; - generated_user_name text; -begin - - -- first we setup the user profile - -- TODO: see if we can get the user's name from the auth.users table once we learn how oauth works - -- TODO: If no name is provided, use the first part of the email address - if new.email IS NOT NULL then - generated_user_name := split_part(new.email, '@', 1); - end if; - - insert into public.profiles (id, name) values (new.id, generated_user_name); - - -- only create the first account if private accounts is enabled - if basejump.is_set('enable_personal_accounts') = true then - -- create the new users's personal account - insert into public.accounts (primary_owner_user_id, personal_account) - values (NEW.id, true) - returning id into first_account_id; - - -- add them to the account_user table so they can act on it - insert into public.account_user (account_id, user_id, account_role) - values (first_account_id, NEW.id, 'owner'); - end if; - return NEW; -end; -$$; - --- trigger the function every time a user is created -create trigger on_auth_user_created - after insert - on auth.users - for each row -execute procedure basejump.run_new_user_setup(); \ No newline at end of file diff --git a/supabase/migrations/00000000000011_invitations.sql b/supabase/migrations/00000000000011_invitations.sql deleted file mode 100644 index 0fdcdfa..0000000 --- a/supabase/migrations/00000000000011_invitations.sql +++ /dev/null @@ -1,166 +0,0 @@ -/** - * Invitation types are either email or link. Email invitations are sent to - * a single user and can only be claimed once. Link invitations can be used multiple times - * Both expire after 24 hours - */ -DROP TYPE IF EXISTS public.invitation_type; - -CREATE TYPE public.invitation_type AS ENUM ('one-time', '24-hour'); -/** - * Invitations are sent to users to join a account - * They pre-define the role the user should have once they join - */ -create table public.invitations -( - -- the id of the invitation - id uuid unique not null default uuid_generate_v4(), - -- what role should invitation accepters be given in this account - account_role account_role not null, - -- the account the invitation is for - account_id uuid references accounts not null, - -- unique token used to accept the invitation - token text unique not null default basejump.generate_token(30), - -- who created the invitation - invited_by_user_id uuid references auth.users not null, - -- account name. filled in by a trigger - account_team_name text, - -- when the invitation was last updated - updated_at timestamp with time zone, - -- when the invitation was created - created_at timestamp with time zone, - -- what type of invitation is this - invitation_type invitation_type not null, - primary key (id) -); - --- manage timestamps - -CREATE TRIGGER set_invitations_timestamp - BEFORE INSERT OR UPDATE - ON public.invitations - FOR EACH ROW -EXECUTE FUNCTION basejump.trigger_set_timestamps(); - -/** - * This funciton fills in account info and inviting user email - * so that the recipient can get more info about the invitation prior to - * accepting. It allows us to avoid complex permissions on accounts - */ -CREATE OR REPLACE FUNCTION basejump.trigger_set_invitation_details() - RETURNS TRIGGER AS -$$ -BEGIN - NEW.invited_by_user_id = auth.uid(); - NEW.account_team_name = (select team_name from public.accounts where id = NEW.account_id); - RETURN NEW; -END -$$ LANGUAGE plpgsql; - -CREATE TRIGGER trigger_set_invitation_details - BEFORE INSERT - ON public.invitations - FOR EACH ROW -EXECUTE FUNCTION basejump.trigger_set_invitation_details(); - --- enable RLS on invitations -alter table public.invitations - enable row level security; - -create policy "Invitations viewable by account owners" on invitations - for select - to authenticated - using ( - created_at > (now() - interval '24 hours') - and - (account_id IN - (SELECT basejump.get_accounts_for_current_user('owner') AS get_accounts_for_current_user)) - ); - - -create policy "Invitations can be created by account owners" on invitations - for insert - to authenticated - with check ( - -- team accounts should be enabled - basejump.is_set('enable_team_accounts') = true - -- this should not be a personal account - and (SELECT personal_account FROM public.accounts WHERE id = account_id) = false - -- the inserting user should be an owner of the account - and - (account_id IN - (SELECT basejump.get_accounts_for_current_user('owner') AS get_accounts_for_current_user)) - ); - -create policy "Invitations can be deleted by account owners" on invitations - for delete - to authenticated - using ( - (account_id IN - (SELECT basejump.get_accounts_for_current_user('owner') AS get_accounts_for_current_user)) - ); - -/** - * Allows a user to accept an existing invitation and join a account - * This one exists in the public schema because we want it to be called - * using the supabase rpc method - */ -create or replace function accept_invitation(lookup_invitation_token text) - returns uuid - language plpgsql - security definer set search_path = public -as -$$ -declare - lookup_account_id uuid; - declare new_member_role account_role; -begin - select account_id, account_role - into lookup_account_id, new_member_role - from invitations - where token = lookup_invitation_token - and created_at > now() - interval '24 hours'; - - if lookup_account_id IS NULL then - raise exception 'Invitation not found'; - end if; - - if lookup_account_id is not null then - -- we've validated the token is real, so grant the user access - insert into account_user (account_id, user_id, account_role) - values (lookup_account_id, auth.uid(), new_member_role); - -- email types of invitations are only good for one usage - delete from invitations where token = lookup_invitation_token and invitation_type = 'one-time'; - end if; - return lookup_account_id; -end; -$$; - -/** - * Allows a user to lookup an existing invitation and join a account - * This one exists in the public schema because we want it to be called - * using the supabase rpc method - */ -create or replace function public.lookup_invitation(lookup_invitation_token text) - returns json - language plpgsql - security definer set search_path = public -as -$$ -declare - team_name text; - invitation_active boolean; -begin - select account_team_name, - case when id IS NOT NULL then true else false end as active - into team_name, invitation_active - from invitations - where token = lookup_invitation_token - and created_at > now() - interval '24 hours' - limit 1; - return json_build_object('active', coalesce(invitation_active, false), 'team_name', team_name); -end; -$$; - - -grant execute on function accept_invitation(text) to authenticated; -grant execute on function lookup_invitation(text) to authenticated; diff --git a/supabase/seed.sql b/supabase/seed.sql deleted file mode 100644 index eade0cd..0000000 --- a/supabase/seed.sql +++ /dev/null @@ -1,12 +0,0 @@ -insert into basejump.config (enable_personal_accounts, - enable_team_accounts, - enable_account_billing, - billing_provider, - stripe_default_trial_period_days, - stripe_default_account_price_id) -values (TRUE, - TRUE, - FALSE, - 'stripe', - 30, - null); \ No newline at end of file diff --git a/supabase/tests/database/1-basejump-schema-tests.sql b/supabase/tests/database/01-basejump-schema-tests.sql similarity index 58% rename from supabase/tests/database/1-basejump-schema-tests.sql rename to supabase/tests/database/01-basejump-schema-tests.sql index 0ea5355..aaefeb8 100644 --- a/supabase/tests/database/1-basejump-schema-tests.sql +++ b/supabase/tests/database/01-basejump-schema-tests.sql @@ -1,29 +1,34 @@ BEGIN; -CREATE EXTENSION "basejump-supabase_test_helpers"; +create extension "basejump-supabase_test_helpers" + version '0.0.2'; -select plan(22); +select plan(24); select has_schema('basejump', 'Basejump schema should exist'); select has_table('basejump', 'config', 'Basejump config table should exist'); +select has_table('basejump', 'accounts', 'Basejump accounts table should exist'); +select has_table('basejump', 'account_user', 'Basejump account_users table should exist'); +select has_table('basejump', 'invitations', 'Basejump invitations table should exist'); +select has_table('basejump', 'billing_customers', 'Basejump billing_customers table should exist'); +select has_table('basejump', 'billing_subscriptions', 'Basejump billing_subscriptions table should exist'); select tests.rls_enabled('public'); select columns_are('basejump', 'config', - Array ['enable_personal_accounts', 'enable_team_accounts', 'enable_account_billing', 'billing_provider', 'stripe_default_trial_period_days', 'stripe_default_account_price_id'], + Array ['enable_team_accounts', 'enable_personal_account_billing', 'enable_team_account_billing', 'billing_provider'], 'Basejump config table should have the correct columns'); -select ok(basejump.is_set('enable_personal_accounts')), 'Basejump config should have personal accounts enabled'; +select ok(basejump.is_set('enable_personal_account_billing')), + 'Basejump config should have personal account billing enabled'; select ok(basejump.is_set('enable_team_accounts')), 'Basejump config should have team accounts enabled'; -select ok((basejump.get_config() ->> 'enable_account_billing')::boolean = false, - 'Basejump config should have account billing disabled'); +select ok((basejump.get_config() ->> 'enable_team_account_billing')::boolean = true, + 'Basejump config should have team account billing enabled'); select ok(basejump.get_config() ->> 'billing_provider' = 'stripe', 'Basejump config should have stripe as the billing provider'); -select ok((basejump.get_config() ->> 'stripe_default_trial_period_days')::int = 30), - 'Basejump config should have a default trial period'; -select function_returns('basejump', 'generate_token', Array ['integer'], 'bytea', +select function_returns('basejump', 'generate_token', Array ['integer'], 'text', 'Basejump generate_token function should exist'); select function_returns('basejump', 'trigger_set_timestamps', 'trigger', 'Basejump trigger_set_timestamps function should exist'); @@ -33,23 +38,21 @@ SELECT schema_privs_are('basejump', 'anon', Array [NULL], 'Anon should not have -- set the role to anonymous for verifying access tests set role anon; select throws_ok('select basejump.get_config()'); -select throws_ok('select basejump.is_set(''enable_personal_accounts'')'); +select throws_ok('select basejump.is_set(''enable_team_accounts'')'); select throws_ok('select basejump.generate_token(1)'); -select throws_ok('select public.get_service_role_config()'); -- set the role to the service_role for testing access set role service_role; -select ok(public.get_service_role_config() is not null), - 'Basejump get_service_role_config should be accessible to the service role'; +select ok(basejump.get_config() is not null), + 'Basejump get_config should be accessible to the service role'; -- set the role to authenticated for tests set role authenticated; select ok(basejump.get_config() is not null), 'Basejump get_config should be accessible to authenticated users'; -select ok(basejump.is_set('enable_personal_accounts')), +select ok(basejump.is_set('enable_team_accounts')), 'Basejump is_set should be accessible to authenticated users'; select ok(basejump.generate_token(1) is not null), 'Basejump generate_token should be accessible to authenticated users'; -select throws_ok('select public.get_service_role_config()'); select isnt_empty('select * from basejump.config', 'authenticated users should have access to Basejump config'); SELECT * diff --git a/supabase/tests/database/2-personal-accounts.sql b/supabase/tests/database/02-personal-accounts.sql similarity index 52% rename from supabase/tests/database/2-personal-accounts.sql rename to supabase/tests/database/02-personal-accounts.sql index 48892d5..8e4e0fd 100644 --- a/supabase/tests/database/2-personal-accounts.sql +++ b/supabase/tests/database/02-personal-accounts.sql @@ -1,17 +1,12 @@ BEGIN; -CREATE EXTENSION "basejump-supabase_test_helpers"; +create extension "basejump-supabase_test_helpers" + version '0.0.2'; select plan(15); -select has_table('public', 'accounts', 'Accounts table should exist'); - --- make sure we're setup for enabling personal accounts -update basejump.config -set enable_personal_accounts = true; - --- we insert a user into auth.users and return the id into user_id to use -select tests.create_supabase_user('test1'); +select tests.create_supabase_user('test1', 'test1@test.com'); select tests.create_supabase_user('test2'); @@ -20,23 +15,23 @@ select tests.create_supabase_user('test2'); ------------ select tests.authenticate_as('test1'); --- should create the personal account automatically +-- should create the personal account automatically with the same ID as the user SELECT row_eq( - $$ select primary_owner_user_id, personal_account from accounts order by created_at desc limit 1 $$, - ROW (tests.get_supabase_uid('test1'), true), + $$ select id, primary_owner_user_id, personal_account, name from basejump.accounts order by created_at desc limit 1 $$, + ROW (tests.get_supabase_uid('test1'), tests.get_supabase_uid('test1'), true, 'test1'::text), 'Inserting a user should create a personal account when personal accounts are enabled' ); -- should add that user to the account as an owner SELECT row_eq( - $$ select user_id, account_id, account_role from account_user $$, - ROW (tests.get_supabase_uid('test1'), (select id from accounts where personal_account = true), 'owner'::account_role), + $$ select user_id, account_id, account_role from basejump.account_user $$, + ROW (tests.get_supabase_uid('test1'), (select id from basejump.accounts where personal_account = true), 'owner'::basejump.account_role), 'Inserting a user should also add an account_user for the created account' ); -- should be able to get your own role for the account SELECT row_eq( - $$ with data as (select id from accounts where personal_account = true) select public.current_user_account_role(data.id) from data $$, + $$ with data as (select id from basejump.accounts where personal_account = true) select public.current_user_account_role(data.id) from data $$, ROW (jsonb_build_object( 'account_role', 'owner', 'is_primary_owner', TRUE, @@ -47,15 +42,15 @@ SELECT row_eq( -- cannot change the accounts.primary_owner_user_id SELECT throws_ok( - $$ update accounts set primary_owner_user_id = '5d94cce7-054f-4d01-a9ec-51e7b7ba8d59' where personal_account = true $$, + $$ update basejump.accounts set primary_owner_user_id = '5d94cce7-054f-4d01-a9ec-51e7b7ba8d59' where personal_account = true $$, 'You do not have permission to update this field' ); -- cannot delete the primary_owner_user_id from the account_user table select row_eq( $$ - delete from account_user where user_id = tests.get_supabase_uid('test1'); - select user_id from account_user where user_id = tests.get_supabase_uid('test1'); + delete from basejump.account_user where user_id = tests.get_supabase_uid('test1'); + select user_id from basejump.account_user where user_id = tests.get_supabase_uid('test1'); $$, ROW (tests.get_supabase_uid('test1')), 'Should not be able to delete the primary_owner_user_id from the account_user table' @@ -63,34 +58,41 @@ select row_eq( -- should not be able to add invitations to personal accounts SELECT throws_ok( - $$ insert into invitations (account_id, account_role, token, invitation_type) values ((select id from accounts where personal_account = true), 'owner', 'test', 'one-time') $$, + $$ insert into basejump.invitations (account_id, account_role, token, invitation_type) values ((select id from basejump.accounts where personal_account = true), 'owner', 'test', 'one_time') $$, 'new row violates row-level security policy for table "invitations"' ); -- should not be able to add new users to personal accounts SELECT throws_ok( - $$ insert into account_user (account_id, account_role, user_id) values ((select id from accounts where personal_account = true), 'owner', '5d94cce7-054f-4d01-a9ec-51e7b7ba8d59') $$, + $$ insert into basejump.account_user (account_id, account_role, user_id) values ((select id from basejump.accounts where personal_account = true), 'owner', '5d94cce7-054f-4d01-a9ec-51e7b7ba8d59') $$, 'new row violates row-level security policy for table "account_user"' ); -- cannot change personal_account setting no matter who you are SELECT throws_ok( - $$ update accounts set personal_account = false where personal_account = true $$, + $$ update basejump.accounts set personal_account = false where personal_account = true $$, 'You do not have permission to update this field' ); -- owner can update their team name SELECT results_eq( - $$ update accounts set team_name = 'test' where id = (select id from accounts where personal_account = true) returning team_name $$, + $$ update basejump.accounts set name = 'test' where id = (select id from basejump.accounts where personal_account = true) returning name $$, $$ select 'test' $$, 'Owner can update their team name' ); --- personal account should be returned by the basejump.get_accounts_for_current_user functoin +-- personal account should be returned by the basejump.get_accounts_with_role function +SELECT results_eq( + $$ select basejump.get_accounts_with_role() $$, + $$ select id from basejump.accounts where personal_account = true $$, + 'Personal account should be returned by the basejump.get_accounts_with_role function' + ); + +-- should get true for personal account using has_role_on_account function SELECT results_eq( - $$ select basejump.get_accounts_for_current_user() $$, - $$ select id from accounts where personal_account = true $$, - 'Personal account should be returned by the basejump.get_accounts_for_current_user function' + $$ select basejump.has_role_on_account((select id from basejump.accounts where personal_account = true), 'owner') $$, + $$ select true $$, + 'Should get true for personal account using has_role_on_account function' ); ----------- @@ -100,14 +102,14 @@ select tests.authenticate_as('test2'); -- non members / owner cannot update team name SELECT results_ne( - $$ update accounts set team_name = 'test' where primary_owner_user_id = tests.get_supabase_uid('test1') returning 1$$, + $$ update basejump.accounts set name = 'test' where primary_owner_user_id = tests.get_supabase_uid('test1') returning 1$$, $$ select 1 $$ ); -- non member / owner should receive no results from accounts SELECT is( (select count(*)::int - from accounts + from basejump.accounts where primary_owner_user_id <> tests.get_supabase_uid('test2')), 0, 'Non members / owner should receive no results from accounts' @@ -120,16 +122,15 @@ SELECT is( select tests.clear_authentication(); -- anonymous should receive no results from accounts -SELECT is_empty( - $$ select * from accounts $$, - 'Anonymous should receive no results from accounts' +SELECT throws_ok( + $$ select * from basejump.accounts $$, + 'permission denied for schema basejump' ); -- anonymous cannot update team name -SELECT results_ne( - $$ update accounts set team_name = 'test' returning 1 $$, - $$ select 1 $$, - 'new row violates row-level security policy for table "invitations"' +SELECT throws_ok( + $$ update basejump.accounts set name = 'test' returning 1 $$, + 'permission denied for schema basejump' ); SELECT * diff --git a/supabase/tests/database/4-team-accounts.sql b/supabase/tests/database/04-team-accounts.sql similarity index 57% rename from supabase/tests/database/4-team-accounts.sql rename to supabase/tests/database/04-team-accounts.sql index bca0fa3..88f5b68 100644 --- a/supabase/tests/database/4-team-accounts.sql +++ b/supabase/tests/database/04-team-accounts.sql @@ -1,9 +1,8 @@ BEGIN; -CREATE EXTENSION "basejump-supabase_test_helpers"; +create extension "basejump-supabase_test_helpers" + version '0.0.2'; -select plan(30); - -select has_table('public', 'accounts', 'Accounts table should exist'); +select plan(34); -- make sure we're setup for enabling personal accounts update basejump.config @@ -20,8 +19,8 @@ select tests.create_supabase_user('test_random_owner'); select tests.authenticate_as('test_random_owner'); -- setup inaccessible tests for a known account ID -insert into accounts (id, team_name, personal_account) -values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', 'nobody in test can access me', false); +insert into basejump.accounts (id, name, slug, personal_account) +values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', 'nobody in test can access me', 'no-access', false); ------------ --- Primary Owner @@ -30,22 +29,22 @@ select tests.authenticate_as('test1'); -- should be able to create a team account when they're enabled SELECT row_eq( - $$ insert into accounts (id, team_name, personal_account) values ('8fcec130-27cd-4374-9e47-3303f9529479', 'test team', false) returning 1$$, + $$ insert into basejump.accounts (id, name, slug, personal_account) values ('8fcec130-27cd-4374-9e47-3303f9529479', 'test team', 'test-team', false) returning 1$$, ROW (1), 'Should be able to create a new team account' ); -- newly created team should be owned by current user SELECT row_eq( - $$ select primary_owner_user_id from accounts where id = '8fcec130-27cd-4374-9e47-3303f9529479' $$, + $$ select primary_owner_user_id from basejump.accounts where id = '8fcec130-27cd-4374-9e47-3303f9529479' $$, ROW (tests.get_supabase_uid('test1')), 'Creating a new team account should make the current user the primary owner' ); -- should add that user to the account as an owner SELECT row_eq( - $$ select user_id, account_role from account_user where account_id = '8fcec130-27cd-4374-9e47-3303f9529479'::uuid $$, - ROW (tests.get_supabase_uid('test1'), 'owner'::account_role), + $$ select user_id, account_role from basejump.account_user where account_id = '8fcec130-27cd-4374-9e47-3303f9529479'::uuid $$, + ROW (tests.get_supabase_uid('test1'), 'owner'::basejump.account_role), 'Inserting an account should also add an account_user for the current user' ); @@ -62,15 +61,15 @@ SELECT row_eq( -- cannot change the accounts.primary_owner_user_id directly SELECT throws_ok( - $$ update accounts set primary_owner_user_id = tests.get_supabase_uid('test2') where personal_account = false $$, + $$ update basejump.accounts set primary_owner_user_id = tests.get_supabase_uid('test2') where personal_account = false $$, 'You do not have permission to update this field' ); -- cannot delete the primary_owner_user_id from the account_user table select row_eq( $$ - delete from account_user where user_id = tests.get_supabase_uid('test1'); - select user_id from account_user where user_id = tests.get_supabase_uid('test1'); + delete from basejump.account_user where user_id = tests.get_supabase_uid('test1'); + select user_id from basejump.account_user where user_id = tests.get_supabase_uid('test1'); $$, ROW (tests.get_supabase_uid('test1')::uuid), 'Should not be able to delete the primary_owner_user_id from the account_user table' @@ -78,46 +77,65 @@ select row_eq( -- owners should be able to add invitations SELECT row_eq( - $$ insert into invitations (account_id, account_role, token, invitation_type) values ('8fcec130-27cd-4374-9e47-3303f9529479', 'member', 'test_member_single_use_token', 'one-time') returning 1 $$, + $$ insert into basejump.invitations (account_id, account_role, token, invitation_type) values ('8fcec130-27cd-4374-9e47-3303f9529479', 'member', 'test_member_single_use_token', 'one_time') returning 1 $$, ROW (1), 'Owners should be able to add invitations for new members' ); SELECT row_eq( - $$ insert into invitations (account_id, account_role, token, invitation_type) values ('8fcec130-27cd-4374-9e47-3303f9529479', 'owner', 'test_owner_single_use_token', 'one-time') returning 1 $$, + $$ insert into basejump.invitations (account_id, account_role, token, invitation_type) values ('8fcec130-27cd-4374-9e47-3303f9529479', 'owner', 'test_owner_single_use_token', 'one_time') returning 1 $$, ROW (1), 'Owners should be able to add invitations for new owners' ); -- should not be able to add new users directly into team accounts SELECT throws_ok( - $$ insert into account_user (account_id, account_role, user_id) values ('8fcec130-27cd-4374-9e47-3303f9529479', 'owner', tests.get_supabase_uid('test2')) $$, + $$ insert into basejump.account_user (account_id, account_role, user_id) values ('8fcec130-27cd-4374-9e47-3303f9529479', 'owner', tests.get_supabase_uid('test2')) $$, 'new row violates row-level security policy for table "account_user"' ); -- cannot change personal_account setting no matter who you are SELECT throws_ok( - $$ update accounts set personal_account = true where id = '8fcec130-27cd-4374-9e47-3303f9529479' $$, + $$ update basejump.accounts set personal_account = true where id = '8fcec130-27cd-4374-9e47-3303f9529479' $$, 'You do not have permission to update this field' ); -- owner can update their team name SELECT results_eq( - $$ update accounts set team_name = 'test' where id = '8fcec130-27cd-4374-9e47-3303f9529479' returning team_name $$, + $$ update basejump.accounts set name = 'test' where id = '8fcec130-27cd-4374-9e47-3303f9529479' returning name $$, $$ values('test') $$, 'Owner can update their team name' ); --- all accounts (personal and team) should be returned by get_accounts_for_current_user test +-- all accounts (personal and team) should be returned by get_accounts_with_role test SELECT ok( - (select '8fcec130-27cd-4374-9e47-3303f9529479' IN (select basejump.get_accounts_for_current_user())), - 'Team account should be returned by the basejump.get_accounts_for_current_user function' + (select '8fcec130-27cd-4374-9e47-3303f9529479' IN + (select basejump.get_accounts_with_role())), + 'Team account should be returned by the basejump.get_accounts_with_role function' ); -- shouoldn't return any accounts if you're not a member of SELECT ok( - (select 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' NOT IN (select basejump.get_accounts_for_current_user())), - 'Team accounts not a member of should NOT be returned by the basejump.get_accounts_for_current_user function' + (select 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' NOT IN + (select basejump.get_accounts_with_role())), + 'Team accounts not a member of should NOT be returned by the basejump.get_accounts_with_role function' + ); + +-- should return true for basejump.has_role_on_account +SELECT ok( + (select basejump.has_role_on_account('8fcec130-27cd-4374-9e47-3303f9529479', 'owner')), + 'Should return true for basejump.has_role_on_account' + ); + +SELECT ok( + (select basejump.has_role_on_account('8fcec130-27cd-4374-9e47-3303f9529479')), + 'Should return true for basejump.has_role_on_account' + ); + +-- should return FALSE when not on the account +SELECT ok( + (select NOT basejump.has_role_on_account('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f')), + 'Should return false for basejump.has_role_on_account' ); ----------- @@ -127,10 +145,10 @@ select tests.clear_authentication(); set role postgres; -- insert account_user for the member test -insert into account_user (account_id, account_role, user_id) +insert into basejump.account_user (account_id, account_role, user_id) values ('8fcec130-27cd-4374-9e47-3303f9529479', 'member', tests.get_supabase_uid('test_member')); -- insert account_user for the owner test -insert into account_user (account_id, account_role, user_id) +insert into basejump.account_user (account_id, account_role, user_id) values ('8fcec130-27cd-4374-9e47-3303f9529479', 'owner', tests.get_supabase_uid('test_owner')); ----------- @@ -140,22 +158,22 @@ select tests.authenticate_as('test_member'); -- should now have access to the account SELECT is( - (select count(*)::int from accounts where id = '8fcec130-27cd-4374-9e47-3303f9529479'), + (select count(*)::int from basejump.accounts where id = '8fcec130-27cd-4374-9e47-3303f9529479'), 1, 'Should now have access to the account' ); -- members cannot update account info SELECT results_ne( - $$ update accounts set team_name = 'test' where id = '8fcec130-27cd-4374-9e47-3303f9529479' returning 1 $$, + $$ update basejump.accounts set name = 'test' where id = '8fcec130-27cd-4374-9e47-3303f9529479' returning 1 $$, $$ values(1) $$, 'Member cannot can update their team name' ); -- account_user should have a role of member SELECT row_eq( - $$ select account_role from account_user where account_id = '8fcec130-27cd-4374-9e47-3303f9529479' and user_id = tests.get_supabase_uid('test_member')$$, - ROW ('member'::account_role), + $$ select account_role from basejump.account_user where account_id = '8fcec130-27cd-4374-9e47-3303f9529479' and user_id = tests.get_supabase_uid('test_member')$$, + ROW ('member'::basejump.account_role), 'Should have the correct account role after accepting an invitation' ); @@ -173,16 +191,28 @@ SELECT row_eq( -- Should NOT show up as an owner in the permissions check SELECT ok( (select '8fcec130-27cd-4374-9e47-3303f9529479' NOT IN - (select basejump.get_accounts_for_current_user('owner'))), - 'Newly added account ID should not be in the list of accounts returned by basejump.get_accounts_for_current_user("owner")' + (select basejump.get_accounts_with_role('owner'))), + 'Newly added account ID should not be in the list of accounts returned by basejump.get_accounts_with_role("owner")' ); -- Should be able ot get a full list of accounts when no permission passed in SELECT ok( - (select '8fcec130-27cd-4374-9e47-3303f9529479' IN (select basejump.get_accounts_for_current_user())), - 'Newly added account ID should be in the list of accounts returned by basejump.get_accounts_for_current_user()' + (select '8fcec130-27cd-4374-9e47-3303f9529479' IN + (select basejump.get_accounts_with_role())), + 'Newly added account ID should be in the list of accounts returned by basejump.get_accounts_with_role()' ); +-- should return true for basejump.has_role_on_account +SELECT ok( + (select basejump.has_role_on_account('8fcec130-27cd-4374-9e47-3303f9529479')), + 'Should return true for basejump.has_role_on_account' + ); + +-- should return false for the owner lookup +SELECT ok( + (select NOT basejump.has_role_on_account('8fcec130-27cd-4374-9e47-3303f9529479', 'owner')), + 'Should return false for basejump.has_role_on_account' + ); ----------- --- Non-Primary Owner @@ -191,15 +221,15 @@ select tests.authenticate_as('test_owner'); -- should now have access to the account SELECT is( - (select count(*)::int from accounts where id = '8fcec130-27cd-4374-9e47-3303f9529479'), + (select count(*)::int from basejump.accounts where id = '8fcec130-27cd-4374-9e47-3303f9529479'), 1, 'Should now have access to the account' ); -- account_user should have a role of member SELECT row_eq( - $$ select account_role from account_user where account_id = '8fcec130-27cd-4374-9e47-3303f9529479' and user_id = tests.get_supabase_uid('test_owner')$$, - ROW ('owner'::account_role), + $$ select account_role from basejump.account_user where account_id = '8fcec130-27cd-4374-9e47-3303f9529479' and user_id = tests.get_supabase_uid('test_owner')$$, + ROW ('owner'::basejump.account_role), 'Should have the expected account role' ); @@ -217,18 +247,19 @@ SELECT row_eq( -- Should NOT show up as an owner in the permissions check SELECT ok( (select '8fcec130-27cd-4374-9e47-3303f9529479' IN - (select basejump.get_accounts_for_current_user('owner'))), - 'Newly added account ID should not be in the list of accounts returned by basejump.get_accounts_for_current_user("owner")' + (select basejump.get_accounts_with_role('owner'))), + 'Newly added account ID should not be in the list of accounts returned by basejump.get_accounts_with_role("owner")' ); -- Should be able ot get a full list of accounts when no permission passed in SELECT ok( - (select '8fcec130-27cd-4374-9e47-3303f9529479' IN (select basejump.get_accounts_for_current_user())), - 'Newly added account ID should be in the list of accounts returned by basejump.get_accounts_for_current_user()' + (select '8fcec130-27cd-4374-9e47-3303f9529479' IN + (select basejump.get_accounts_with_role())), + 'Newly added account ID should be in the list of accounts returned by basejump.get_accounts_with_role()' ); SELECT results_eq( - $$ update accounts set team_name = 'test2' where id = '8fcec130-27cd-4374-9e47-3303f9529479' returning team_name $$, + $$ update basejump.accounts set name = 'test2' where id = '8fcec130-27cd-4374-9e47-3303f9529479' returning name $$, $$ values('test2') $$, 'New owners can update their team name' ); @@ -241,12 +272,12 @@ select tests.authenticate_as('test2'); -- non members / owner cannot update team name SELECT results_ne( - $$ update accounts set team_name = 'test3' where id = '8fcec130-27cd-4374-9e47-3303f9529479' returning 1$$, + $$ update basejump.accounts set name = 'test3' where id = '8fcec130-27cd-4374-9e47-3303f9529479' returning 1$$, $$ select 1 $$ ); -- non member / owner should receive no results from accounts SELECT is( - (select count(*)::int from accounts where personal_account = false), + (select count(*)::int from basejump.accounts where personal_account = false), 0, 'Non members / owner should receive no results from accounts' ); @@ -257,16 +288,15 @@ SELECT is( select tests.clear_authentication(); -- anonymous should receive no results from accounts -SELECT is_empty( - $$ select * from accounts $$, - 'Anonymous should receive no results from accounts' +SELECT throws_ok( + $$ select * from basejump.accounts $$, + 'permission denied for schema basejump' ); -- anonymous cannot update team name -SELECT results_ne( - $$ update accounts set team_name = 'test' returning 1 $$, - $$ select 1 $$, - 'new row violates row-level security policy for table "invitations"' +SELECT throws_ok( + $$ update basejump.accounts set name = 'test' returning 1 $$, + 'permission denied for schema basejump' ); SELECT * diff --git a/supabase/tests/database/5-team-accounts-disabled.sql b/supabase/tests/database/05-team-accounts-disabled.sql similarity index 75% rename from supabase/tests/database/5-team-accounts-disabled.sql rename to supabase/tests/database/05-team-accounts-disabled.sql index d45e8fd..5d0cad0 100644 --- a/supabase/tests/database/5-team-accounts-disabled.sql +++ b/supabase/tests/database/05-team-accounts-disabled.sql @@ -1,5 +1,6 @@ BEGIN; -CREATE EXTENSION "basejump-supabase_test_helpers"; +create extension "basejump-supabase_test_helpers" + version '0.0.2'; select plan(1); @@ -17,7 +18,7 @@ select tests.authenticate_as('test1'); -- check to see if we can create an accoiunt select throws_ok( - $$ insert into accounts (team_name, personal_account) values ('test team', false) $$, + $$ insert into basejump.accounts (name, personal_account) values ('test team', false) $$, 'new row violates row-level security policy for table "accounts"' ); diff --git a/supabase/tests/database/6-invitations.sql b/supabase/tests/database/06-invitations.sql similarity index 50% rename from supabase/tests/database/6-invitations.sql rename to supabase/tests/database/06-invitations.sql index 1590ab6..5cede9b 100644 --- a/supabase/tests/database/6-invitations.sql +++ b/supabase/tests/database/06-invitations.sql @@ -1,16 +1,17 @@ -- the main testing for invitations on accounts is in the team_accounts tests --- this batch is to let us test the more complicated behaviors such as one-time, 24-hour, multiple use, etc...accounts +-- this batch is to let us test the more complicated behaviors such as one_time, 24_hour, multiple use, etc...accounts BEGIN; -CREATE EXTENSION "basejump-supabase_test_helpers"; +create extension "basejump-supabase_test_helpers" + version '0.0.2'; -select plan(28); +select plan(35); -- make sure we're setup for enabling personal accounts update basejump.config set enable_team_accounts = true; -- create the users we need for testing -select tests.create_supabase_user('test1'); +select tests.create_supabase_user('owner'); select tests.create_supabase_user('stranger'); select tests.create_supabase_user('invited_member1'); select tests.create_supabase_user('invited_member2'); @@ -18,41 +19,82 @@ select tests.create_supabase_user('invited_member3'); select tests.create_supabase_user('expired'); --- start acting as an authenticated user -select tests.authenticate_as('test1'); +select tests.authenticate_as('owner'); -- create the taem account -insert into accounts (id, team_name, personal_account) -values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', 'test', false); +insert into basejump.accounts (id, name, slug, personal_account) +values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', 'test', 'test', false); -- insert some invitations SELECT row_eq( - $$ insert into invitations (account_id, account_role, token, invitation_type) values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', 'owner', 'test_member_single_use_token', 'one-time') returning 1 $$, + $$ insert into basejump.invitations (account_id, account_role, token, invitation_type) values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', 'owner', 'test_member_single_use_token', 'one_time') returning 1 $$, ROW (1), - 'Owners should be able to add one-time invitations for new members' + 'Owners should be able to add one_time invitations for new members' ); SELECT row_eq( - $$ insert into invitations (account_id, account_role, token, invitation_type) values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', 'member', 'test_member_24_hour_token', '24-hour') returning 1 $$, + $$ insert into basejump.invitations (account_id, account_role, token, invitation_type) values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', 'member', 'test_member_24_hour_token', '24_hour') returning 1 $$, ROW (1), - 'Owners should be able to add 24-hour invitations for new members' + 'Owners should be able to add 24_hour invitations for new members' ); +-- Creating an invitation with the create_invitation function should also work +SELECT row_eq( + $$ select json_object_keys(create_invitation('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f'::uuid, 'member'::basejump.account_role, '24_hour'::basejump.invitation_type))$$, + ROW ('token'::text), + 'Owners should be able to add 24_hour invitations for new members with create_invitation' + ); + +-- listing invitations should work +SELECT row_eq( + $$ select json_array_length(get_account_invitations('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f')) $$, + ROW (3), + 'Should be able to list invitations for an account as an owner' + ); + +------- +-- Deleting invitations +------- + +insert into basejump.invitations (account_id, account_role, token, invitation_type, id) +values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', 'member', 'test_member_multiple_use_token', 'one_time', + '5c795311-62cc-4e44-b056-8d2ac632d0bf'); + +select tests.authenticate_as('invited_member1'); +select throws_ok( + $$ select delete_invitation('5c795311-62cc-4e44-b056-8d2ac632d0bf') $$, + 'Only account owners can delete invitations' + ); + +--- owner can delete invitations +select tests.authenticate_as('owner'); +SELECT row_eq( + $$ select json_array_length(get_account_invitations('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f')) $$, + ROW (4), + 'Should be able to list invitations for an account as an owner' + ); +select delete_invitation('5c795311-62cc-4e44-b056-8d2ac632d0bf'); +SELECT row_eq( + $$ select json_array_length(get_account_invitations('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f')) $$, + ROW (3), + 'Should be able to list invitations for an account as an owner' + ); ---------- --- Team member 1 joining with one-time token +-- Team member 1 joining with one_time token ---------- select tests.authenticate_as('invited_member1'); -- should be able to lookup an invitation I know the token for SELECT results_eq( $$ select lookup_invitation('test_member_single_use_token')::text $$, - $$ select json_build_object('active', true, 'team_name', 'test')::text $$, + $$ select json_build_object('active', true, 'account_name', 'test')::text $$, 'Should be able to lookup an invitation I know the token for' ); -- should be able to lookup an invitation I know the token for SELECT results_eq( $$ select lookup_invitation('not-a-real-token')::text $$, - $$ select json_build_object('active', false, 'team_name', null)::text $$, + $$ select json_build_object('active', false, 'account_name', null)::text $$, 'Fake tokens should fail lookup gracefully' ); @@ -68,33 +110,40 @@ SELECT lives_ok( 'Should be able to accept an invitation' ); --- should be able to get the team from get_accounts_for_current_user +-- should be able to get the team from get_accounts_with_role SELECT ok( (select 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' IN - (select basejump.get_accounts_for_current_user('owner'))), + (select basejump.get_accounts_with_role('owner'))), 'Should now be a part of the team as owner' ); SELECT ok( (select 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' NOT IN - (select basejump.get_accounts_for_current_user('member'))), + (select basejump.get_accounts_with_role('member'))), 'Should not be part of the team as member role' ); SELECT ok( - (select 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' IN (select basejump.get_accounts_for_current_user())), + (select 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' IN + (select basejump.get_accounts_with_role())), 'Should now be a part of the team as lookup all' ); +SELECT row_eq( + $$ select json_array_length(get_account_invitations('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f')) $$, + ROW (2), + 'Should be able to list invitations for an account as an owner' + ); + ------------ --- Second member joining with 24-hour token +-- Second member joining with 24_hour token ------------ select tests.authenticate_as('invited_member2'); -- should not be able to lookup a consumed token SELECT row_eq( $$ select lookup_invitation('test_member_single_use_token')::text $$, - ROW (json_build_object('active', false, 'team_name', null)::text), + ROW (json_build_object('active', false, 'account_name', null)::text), 'Should not be able to lookup a consumed token' ); @@ -107,73 +156,79 @@ SELECT throws_ok( -- should be able to lookup an invitation I know the token for SELECT results_eq( $$ select lookup_invitation('test_member_24_hour_token')::text $$, - $$ select json_build_object('active', true, 'team_name', 'test')::text $$, + $$ select json_build_object('active', true, 'account_name', 'test')::text $$, 'Should be able to lookup an invitation I know the token for' ); -- should be able to accept an invitation SELECT lives_ok( $$ select accept_invitation('test_member_24_hour_token') $$, - 'Should be able to accept a 24-hour invitation' + 'Should be able to accept a 24_hour invitation' ); --- should be able to get the team from get_accounts_for_current_user +-- should be able to get the team from get_accounts_with_role SELECT ok( (select 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' NOT IN - (select basejump.get_accounts_for_current_user('owner'))), + (select basejump.get_accounts_with_role('owner'))), 'Should not be a part of the team as owner' ); SELECT ok( (select 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' IN - (select basejump.get_accounts_for_current_user('member'))), + (select basejump.get_accounts_with_role('member'))), 'Should be part of the team as member role' ); SELECT ok( - (select 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' IN (select basejump.get_accounts_for_current_user())), + (select 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' IN + (select basejump.get_accounts_with_role())), 'Should now be a part of the team as lookup all' ); -- members should NOT be able to create new invitations SELECT throws_ok( - $$ insert into invitations (account_id, account_role, token, invitation_type) values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', 'member', 'test_permissions_token', 'one-time') returning 1 $$, + $$ insert into basejump.invitations (account_id, account_role, token, invitation_type) values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', 'member', 'test_permissions_token', 'one_time') returning 1 $$, 'new row violates row-level security policy for table "invitations"' ); +SELECT throws_ok( + $$ select json_array_length(get_account_invitations('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f')) $$, + 'Only account owners can access this function' + ); ------------ --- Third member joining with 24-hour token +-- Third member joining with 24_hour token ------------ select tests.authenticate_as('invited_member3'); -- should be able to lookup an invitation I know the token for SELECT results_eq( $$ select lookup_invitation('test_member_24_hour_token')::text $$, - $$ select json_build_object('active', true, 'team_name', 'test')::text $$, + $$ select json_build_object('active', true, 'account_name', 'test')::text $$, 'Should be able to lookup an invitation I know the token for' ); -- should be able to accept an invitation SELECT lives_ok( $$ select accept_invitation('test_member_24_hour_token') $$, - 'Should be able to accept a 24-hour invitation' + 'Should be able to accept a 24_hour invitation' ); --- should be able to get the team from get_accounts_for_current_user +-- should be able to get the team from get_accounts_with_role SELECT ok( (select 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' NOT IN - (select basejump.get_accounts_for_current_user('owner'))), + (select basejump.get_accounts_with_role('owner'))), 'Should not be a part of the team as owner' ); SELECT ok( (select 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' IN - (select basejump.get_accounts_for_current_user('member'))), + (select basejump.get_accounts_with_role('member'))), 'Should be part of the team as member role' ); SELECT ok( - (select 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' IN (select basejump.get_accounts_for_current_user())), + (select 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' IN + (select basejump.get_accounts_with_role())), 'Should now be a part of the team as lookup all' ); @@ -184,13 +239,13 @@ select tests.authenticate_as('stranger'); -- should not find any invitations SELECT is_empty( - $$ select * from invitations $$, + $$ select * from basejump.invitations $$, 'Should not have access to any invitations' ); -- inserting an invitation for an account ID you know but aren't an owner of should NOT work SELECT throws_ok( - $$ insert into invitations (account_id, account_role, token, invitation_type) values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', 'owner', 'test', 'one-time') returning 1 $$, + $$ insert into basejump.invitations (account_id, account_role, token, invitation_type) values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', 'owner', 'test', 'one_time') returning 1 $$, 'new row violates row-level security policy for table "invitations"' ); @@ -201,32 +256,32 @@ SELECT throws_ok( select tests.clear_authentication(); -- should not find any invitations -SELECT is_empty( - $$ select * from invitations $$, - 'Should not have access to any invitations' +SELECT throws_ok( + $$ select * from basejump.invitations $$, + 'permission denied for schema basejump' ); -- cannot create an invitation as an anonymous user for a known account ID SELECT throws_ok( - $$ insert into invitations (account_id, account_role, token, invitation_type) values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', 'owner', 'test', 'one-time') returning 1 $$, - 'new row violates row-level security policy for table "invitations"' + $$ insert into basejump.invitations (account_id, account_role, token, invitation_type) values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', 'owner', 'test', 'one_time') returning 1 $$, + 'permission denied for schema basejump' ); ----------- --- Expired 24-hour tokens +-- Expired 24_hour tokens ----------- set local role postgres; -- we need to remove the invitations timestamp trigger so we can force the token to expire -drop trigger set_invitations_timestamp ON public.invitations; +drop trigger basejump_set_invitations_timestamp ON basejump.invitations; -select tests.authenticate_as('test1'); +select tests.authenticate_as('owner'); -insert into invitations (account_id, account_role, token, invitation_type, created_at, updated_at) +insert into basejump.invitations (account_id, account_role, token, invitation_type, created_at, updated_at) values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', 'owner', 'expired_token', - '24-hour', + '24_hour', now() - interval '25 hours', now() - interval '25 hours'); @@ -235,7 +290,7 @@ select tests.authenticate_as('expired'); -- should not be able to lookup an expired token SELECT row_eq( $$ select lookup_invitation('expired_token')::text $$, - ROW (json_build_object('active', false, 'team_name', null)::text), + ROW (json_build_object('active', false, 'account_name', null)::text), 'Should not be able to lookup an expired token' ); diff --git a/supabase/tests/database/7-inviting-team-member.sql b/supabase/tests/database/07-inviting-team-member.sql similarity index 59% rename from supabase/tests/database/7-inviting-team-member.sql rename to supabase/tests/database/07-inviting-team-member.sql index 1e4869e..cfceb57 100644 --- a/supabase/tests/database/7-inviting-team-member.sql +++ b/supabase/tests/database/07-inviting-team-member.sql @@ -1,7 +1,8 @@ BEGIN; -CREATE EXTENSION "basejump-supabase_test_helpers"; +create extension "basejump-supabase_test_helpers" + version '0.0.2'; -select plan(8); +select plan(10); -- make sure we're setup for enabling personal accounts update basejump.config @@ -14,12 +15,12 @@ select tests.create_supabase_user('invited'); --- start acting as an authenticated user select tests.authenticate_as('test1'); -insert into accounts (id, team_name, personal_account) -values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', 'test', false); +insert into basejump.accounts (id, name, slug, personal_account) +values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', 'test', 'test', false); -- create invitation SELECT row_eq( - $$ insert into invitations (account_id, account_role, token, invitation_type) values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', 'member', 'test_member_single_use_token', 'one-time') returning 1 $$, + $$ insert into basejump.invitations (account_id, account_role, token, invitation_type) values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', 'member', 'test_member_single_use_token', 'one_time') returning 1 $$, ROW (1), 'Owners should be able to add invitations for new members' ); @@ -29,7 +30,7 @@ select tests.authenticate_as('invited'); -- should NOT be able to lookup invitations directly SELECT is( - (select count(*)::int from invitations), + (select count(*)::int from basejump.invitations), 0, 'Cannot load invitations directly' ); @@ -39,7 +40,7 @@ SELECT row_eq( $$ select lookup_invitation('test_member_single_use_token')::text $$, ROW (json_build_object( 'active', true, - 'team_name', 'test')::text + 'account_name', 'test')::text ), 'Should be able to lookup an invitation I know the token for' ); @@ -49,7 +50,7 @@ SELECT row_eq( $$ select lookup_invitation('not-real-token')::text $$, ROW (json_build_object( 'active', false, - 'team_name', null)::text + 'account_name', null)::text ), 'Fake tokens should fail lookup gracefully' ); @@ -66,19 +67,32 @@ SELECT lives_ok( 'Should be able to accept an invitation' ); --- should be able to get the team from get_accounts_for_current_user +-- should be able to get the team from get_accounts_with_role SELECT ok( - (select 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' IN (select basejump.get_accounts_for_current_user())), + (select 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' IN + (select basejump.get_accounts_with_role())), 'Should now be a part of the team' ); -- should have the correct role on the team SELECT row_eq( - $$ select account_role from account_user where account_id = 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f'::uuid and user_id = tests.get_supabase_uid('invited') $$, - ROW ('member'::account_role), + $$ select account_role from basejump.account_user where account_id = 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f'::uuid and user_id = tests.get_supabase_uid('invited') $$, + ROW ('member'::basejump.account_role), 'Should have the correct account role after accepting an invitation' ); +SELECT throws_ok( + $$ select get_account_members('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f')$$, + 'Only account owners can access this function' + ); + +select tests.authenticate_as('test1'); +SELECT row_eq( + $$ select json_array_length(get_account_members('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f')) $$, + ROW (2), + 'Should be able to get account members as owner' + ); + SELECT * FROM finish(); diff --git a/supabase/tests/database/8-inviting-team-owner.sql b/supabase/tests/database/08-inviting-team-owner.sql similarity index 67% rename from supabase/tests/database/8-inviting-team-owner.sql rename to supabase/tests/database/08-inviting-team-owner.sql index f4eeb6f..647dbad 100644 --- a/supabase/tests/database/8-inviting-team-owner.sql +++ b/supabase/tests/database/08-inviting-team-owner.sql @@ -1,5 +1,6 @@ BEGIN; -CREATE EXTENSION "basejump-supabase_test_helpers"; +create extension "basejump-supabase_test_helpers" + version '0.0.2'; select plan(9); @@ -14,12 +15,12 @@ select tests.create_supabase_user('invited'); --- start acting as an authenticated user select tests.authenticate_as('test1'); -insert into accounts (id, team_name, personal_account) -values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', 'test', false); +insert into basejump.accounts (id, name, slug, personal_account) +values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', 'test', 'test', false); -- create invitation SELECT row_eq( - $$ insert into invitations (account_id, account_role, token, invitation_type) values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', 'owner', 'test_owner_single_use_token', 'one-time') returning 1 $$, + $$ insert into basejump.invitations (account_id, account_role, token, invitation_type) values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', 'owner', 'test_owner_single_use_token', 'one_time') returning 1 $$, ROW (1), 'Owners should be able to add invitations for new owners' ); @@ -29,7 +30,7 @@ select tests.authenticate_as('invited'); -- should NOT be able to lookup invitations directly SELECT is( - (select count(*)::int from invitations), + (select count(*)::int from basejump.invitations), 0, 'Cannot load invitations directly' ); @@ -39,7 +40,7 @@ SELECT row_eq( $$ select lookup_invitation('test_owner_single_use_token')::text $$, ROW (json_build_object( 'active', true, - 'team_name', 'test')::text + 'account_name', 'test')::text ), 'Should be able to lookup an invitation I know the token for' ); @@ -49,7 +50,7 @@ SELECT row_eq( $$ select lookup_invitation('not-real-token')::text $$, ROW (json_build_object( 'active', false, - 'team_name', null)::text + 'account_name', null)::text ), 'Fake tokens should fail lookup gracefully' ); @@ -66,23 +67,24 @@ SELECT lives_ok( 'Should be able to accept an invitation' ); --- should be able to get the team from get_accounts_for_current_user +-- should be able to get the team from get_accounts_with_role SELECT ok( - (select 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' IN (select basejump.get_accounts_for_current_user())), + (select 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' IN + (select basejump.get_accounts_with_role())), 'Should now be a part of the team' ); --- should be able to get the team from get_accounts_for_current_user +-- should be able to get the team from get_accounts_with_role SELECT ok( (select 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' IN - (select basejump.get_accounts_for_current_user('owner'))), + (select basejump.get_accounts_with_role('owner'))), 'Should now be a part of the team as an owner' ); -- should have the correct role on the team SELECT row_eq( - $$ select account_role from account_user where account_id = 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f'::uuid and user_id = tests.get_supabase_uid('invited') $$, - ROW ('owner'::account_role), + $$ select account_role from basejump.account_user where account_id = 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f'::uuid and user_id = tests.get_supabase_uid('invited') $$, + ROW ('owner'::basejump.account_role), 'Should have the correct account role after accepting an invitation' ); diff --git a/supabase/tests/database/09-removing-team-members.sql b/supabase/tests/database/09-removing-team-members.sql new file mode 100644 index 0000000..db6a325 --- /dev/null +++ b/supabase/tests/database/09-removing-team-members.sql @@ -0,0 +1,86 @@ +BEGIN; +create extension "basejump-supabase_test_helpers" + version '0.0.2'; + +select plan(6); + +-- make sure we're setup for enabling personal accounts +update basejump.config +set enable_team_accounts = true; + +-- create the users we need for testing +select tests.create_supabase_user('primary_owner'); +select tests.create_supabase_user('invited_owner'); +select tests.create_supabase_user('member'); +select tests.create_supabase_user('testing_member'); + +--- Setup the tests +select tests.authenticate_as('primary_owner'); +select create_account('test', 'Test Account'); + +set role postgres; + +insert into basejump.account_user (account_id, account_role, user_id) +values (get_account_id('test'), 'member', tests.get_supabase_uid('member')); +insert into basejump.account_user (account_id, account_role, user_id) +values (get_account_id('test'), 'owner', tests.get_supabase_uid('invited_owner')); +insert into basejump.account_user (account_id, account_role, user_id) +values (get_account_id('test'), 'member', tests.get_supabase_uid('testing_member')); + +--- can NOT remove a member unless your an owner +select tests.authenticate_as('member'); + +SELECT throws_ok( + $$ select remove_account_member(get_account_id('test'), tests.get_supabase_uid('testing_member')) $$, + 'Only account owners can access this function' + ); + +--- CAN remove a member if you're an owner +select tests.authenticate_as('invited_owner'); + +select lives_ok( + $$select remove_account_member(get_account_id('test'), tests.get_supabase_uid('testing_member'))$$, + 'Owners should be able to remove members' + ); + +select tests.authenticate_as('testing_member'); + +SELECT is( + (select basejump.has_role_on_account(get_account_id('test'))), + false, + 'Should no longer have access to the account' + ); + +--- can NOT remove the primary owner +select tests.authenticate_as('invited_owner'); + +--- attempt to delete primary owner +select remove_account_member(get_account_id('test'), tests.get_supabase_uid('primary_owner')); + +--- CAN remove ANOTHER owner as an owner as long as that owner is NOT the primary owner + +select tests.authenticate_as('primary_owner'); + +SELECT is( + (select basejump.has_role_on_account(get_account_id('test'), 'owner')), + true, + 'Primary owner should still be on the account' + ); + +select lives_ok( + $$select remove_account_member(get_account_id('test'), tests.get_supabase_uid('invited_owner'))$$, + 'Owners should be able to remove owners that arent the primary' + ); + +select tests.authenticate_as('invited_owner'); + +SELECT is( + (select basejump.has_role_on_account(get_account_id('test'))), + false, + 'Should no longer have access to the account' + ); + +SELECT * +FROM finish(); + +ROLLBACK; \ No newline at end of file diff --git a/supabase/tests/database/10-account-roles.sql b/supabase/tests/database/10-account-roles.sql index 91634cc..ec83478 100644 --- a/supabase/tests/database/10-account-roles.sql +++ b/supabase/tests/database/10-account-roles.sql @@ -1,5 +1,6 @@ BEGIN; -CREATE EXTENSION "basejump-supabase_test_helpers"; +create extension "basejump-supabase_test_helpers" + version '0.0.2'; select plan(17); -- make sure we're setup for enabling personal accounts @@ -14,14 +15,14 @@ select tests.create_supabase_user('member'); --- start acting as an authenticated user select tests.authenticate_as('primary'); -insert into accounts (id, team_name, personal_account) -values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', 'test', false); +insert into basejump.accounts (id, name, slug, personal_account) +values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', 'test', 'test', false); -- setup users for tests set local role postgres; -insert into account_user (account_id, user_id, account_role) +insert into basejump.account_user (account_id, user_id, account_role) values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', tests.get_supabase_uid('owner'), 'owner'); -insert into account_user (account_id, user_id, account_role) +insert into basejump.account_user (account_id, user_id, account_role) values ('d126ecef-35f6-4b5d-9f28-d9f00a9fb46f', tests.get_supabase_uid('member'), 'member'); -------- @@ -31,7 +32,7 @@ select tests.authenticate_as('member'); -- can't update role directly in the account_user table SELECT results_ne( - $$ update account_user set account_role = 'owner' where account_id = 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' and user_id = tests.get_supabase_uid('member') returning 1 $$, + $$ update basejump.account_user set account_role = 'owner' where account_id = 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' and user_id = tests.get_supabase_uid('member') returning 1 $$, $$ values(1) $$, 'Members should not be able to update their own role' ); @@ -44,8 +45,8 @@ SELECT throws_ok( -- member should still be only a member SELECT row_eq( - $$ select account_role from account_user where account_id = 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' and user_id = tests.get_supabase_uid('member') $$, - ROW ('member'::account_role), + $$ select account_role from basejump.account_user where account_id = 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' and user_id = tests.get_supabase_uid('member') $$, + ROW ('member'::basejump.account_role), 'Member should still be a member' ); @@ -56,7 +57,7 @@ select tests.authenticate_as('owner'); -- can't update role directly in the account_user table SELECT results_ne( - $$ update account_user set account_role = 'owner' where account_id = 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' and user_id = tests.get_supabase_uid('member') returning 1 $$, + $$ update basejump.account_user set account_role = 'owner' where account_id = 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' and user_id = tests.get_supabase_uid('member') returning 1 $$, $$ values(1) $$, 'Members should not be able to update their own role' ); @@ -68,8 +69,8 @@ SELECT throws_ok( ); SELECT row_eq( - $$ select account_role from account_user where account_id = 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' and user_id = tests.get_supabase_uid('member') $$, - ROW ('member'::account_role), + $$ select account_role from basejump.account_user where account_id = 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' and user_id = tests.get_supabase_uid('member') $$, + ROW ('member'::basejump.account_role), 'Member should still be a member since primary owner change failed' ); @@ -82,14 +83,14 @@ SELECT throws_ok( --- primary owner should still be the same SELECT row_eq( - $$ select account_role from account_user where account_id = 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' and user_id = tests.get_supabase_uid('primary') $$, - ROW ('owner'::account_role), + $$ select account_role from basejump.account_user where account_id = 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' and user_id = tests.get_supabase_uid('primary') $$, + ROW ('owner'::basejump.account_role), 'Primary owner should still be the same' ); -- account should have the same primary_owner_user_id SELECT row_eq( - $$ select primary_owner_user_id from accounts where id = 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' $$, + $$ select primary_owner_user_id from basejump.accounts where id = 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' $$, ROW (tests.get_supabase_uid('primary')), 'Primary owner should still be the same' ); @@ -102,8 +103,8 @@ SELECT lives_ok( -- member should now be an owner SELECT row_eq( - $$ select account_role from account_user where account_id = 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' and user_id = tests.get_supabase_uid('member') $$, - ROW ('owner'::account_role), + $$ select account_role from basejump.account_user where account_id = 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' and user_id = tests.get_supabase_uid('member') $$, + ROW ('owner'::basejump.account_role), 'Member should now be an owner' ); @@ -114,7 +115,7 @@ select tests.authenticate_as('primary'); -- can't update role directly in the account_user table SELECT results_ne( - $$ update account_user set account_role = 'member' where account_id = 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' and user_id = tests.get_supabase_uid('member') returning 1 $$, + $$ update basejump.account_user set account_role = 'member' where account_id = 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' and user_id = tests.get_supabase_uid('member') returning 1 $$, $$ values(1) $$, 'Members should not be able to update their own role' ); @@ -127,8 +128,8 @@ SELECT lives_ok( -- member should now be a member SELECT row_eq( - $$ select account_role from account_user where account_id = 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' and user_id = tests.get_supabase_uid('member') $$, - ROW ('member'::account_role), + $$ select account_role from basejump.account_user where account_id = 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' and user_id = tests.get_supabase_uid('member') $$, + ROW ('member'::basejump.account_role), 'Member should now be a member' ); @@ -140,14 +141,14 @@ SELECT lives_ok( -- member should now be a primary owner SELECT row_eq( - $$ select account_role from account_user where account_id = 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' and user_id = tests.get_supabase_uid('member') $$, - ROW ('owner'::account_role), + $$ select account_role from basejump.account_user where account_id = 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' and user_id = tests.get_supabase_uid('member') $$, + ROW ('owner'::basejump.account_role), 'Member should now be a primary owner' ); -- account primary_owner_user_id should be updated SELECT row_eq( - $$ select primary_owner_user_id from accounts where id = 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' $$, + $$ select primary_owner_user_id from basejump.accounts where id = 'd126ecef-35f6-4b5d-9f28-d9f00a9fb46f' $$, ROW (tests.get_supabase_uid('member')), 'Primary owner should be updated' ); diff --git a/supabase/tests/database/11-public-account-functions.sql b/supabase/tests/database/11-public-account-functions.sql new file mode 100644 index 0000000..bfa1238 --- /dev/null +++ b/supabase/tests/database/11-public-account-functions.sql @@ -0,0 +1,252 @@ +BEGIN; +create extension "basejump-supabase_test_helpers" + version '0.0.2'; + +select plan(29); + +-- make sure we're setup for the test correctly +update basejump.config +set enable_team_accounts = true; + +--- we insert a user into auth.users and return the id into user_id to use +select tests.create_supabase_user('test1'); +select tests.create_supabase_user('test2'); +select tests.create_supabase_user('test_member'); + +select tests.authenticate_as('test1'); +select create_account('my-account', 'My account'); +select create_account(name => 'My Account 2', slug => 'my-account-2'); + +select is( + (select (get_account_by_slug('my-account') ->> 'account_id')::uuid), + (select id from basejump.accounts where slug = 'my-account'), + 'get_account_by_slug returns the correct account_id' + ); + +select is( + (select json_array_length(get_accounts())), + 3, + 'get_accounts returns 2 accounts' + ); + + +-- insert known account id into accounts table for testing later +insert into basejump.accounts (id, slug, name) +values ('00000000-0000-0000-0000-000000000000', 'my-known-account', 'My Known Account'); + +-- get_account_id should return the correct account id +select is( + (select public.get_account_id('my-known-account')), + '00000000-0000-0000-0000-000000000000'::uuid, + 'get_account_id should return the correct id' + ); + +select is( + (select (public.get_account('00000000-0000-0000-0000-000000000000') ->> 'account_id')::uuid), + '00000000-0000-0000-0000-000000000000'::uuid, + 'get_account should be able to return a known account' + ); + +----- updating accounts should work +select update_account('00000000-0000-0000-0000-000000000000', slug => 'my-updated-slug'); + +select is( + (select slug from basejump.accounts where id = '00000000-0000-0000-0000-000000000000'), + 'my-updated-slug', + 'Updating slug should have been successful for the owner' + ); + +select update_account('00000000-0000-0000-0000-000000000000', name => 'My Updated Account Name'); + +select is( + (select name from basejump.accounts where id = '00000000-0000-0000-0000-000000000000'), + 'My Updated Account Name', + 'Updating team name should have been successful for the owner' + ); + +select update_account('00000000-0000-0000-0000-000000000000', public_metadata => jsonb_build_object('foo', 'bar')); + +select is( + (select public_metadata from basejump.accounts where id = '00000000-0000-0000-0000-000000000000'), + '{ + "foo": "bar" + }'::jsonb, + 'Updating meta should have been successful for the owner' + ); + +select update_account('00000000-0000-0000-0000-000000000000', public_metadata => jsonb_build_object('foo', 'bar2')); + +select is( + (select public_metadata from basejump.accounts where id = '00000000-0000-0000-0000-000000000000'), + '{ + "foo": "bar2" + }'::jsonb, + 'Updating meta should have been successful for the owner' + ); + +select update_account('00000000-0000-0000-0000-000000000000', public_metadata => jsonb_build_object('foo2', 'bar')); + +select is( + (select public_metadata from basejump.accounts where id = '00000000-0000-0000-0000-000000000000'), + '{ + "foo": "bar2", + "foo2": "bar" + }'::jsonb, + 'Updating meta should have merged by default' + ); + +select update_account('00000000-0000-0000-0000-000000000000', public_metadata => jsonb_build_object('foo3', 'bar'), + replace_metadata => true); + +select is( + (select public_metadata from basejump.accounts where id = '00000000-0000-0000-0000-000000000000'), + '{ + "foo3": "bar" + }'::jsonb, + 'Updating meta should support replacing when you want' + ); + +-- get_account should return public metadata +select is( + (select (get_account('00000000-0000-0000-0000-000000000000') ->> 'metadata')::jsonb), + '{ + "foo3": "bar" + }'::jsonb, + 'get_account should return public metadata' + ); + +select update_account('00000000-0000-0000-0000-000000000000', name => 'My Updated Account Name 2'); + +select is( + (select public_metadata from basejump.accounts where id = '00000000-0000-0000-0000-000000000000'), + '{ + "foo3": "bar" + }'::jsonb, + 'Updating other fields should not affect public metadata' + ); + +--- test that we cannot update accounts we belong to but don't own + +select tests.clear_authentication(); +set role postgres; + +insert into basejump.account_user (account_id, account_role, user_id) +values ('00000000-0000-0000-0000-000000000000', 'member', tests.get_supabase_uid('test_member')); + +select tests.authenticate_as('test_member'); + +select throws_ok( + $$select update_account('00000000-0000-0000-0000-000000000000', slug => 'my-updated-slug-200')$$, + 'Only account owners can update an account' + ); +------- +--- Second user +------- + +select tests.authenticate_as('test2'); + +select throws_ok( + $$select get_account('00000000-0000-0000-0000-000000000000')$$, + 'Not found' + ); + +select throws_ok( + $$select get_account_by_slug('my-known-account')$$, + 'Not found' + ); + +select throws_ok( + $$select current_user_account_role('00000000-0000-0000-0000-000000000000')$$, + 'Not found' + ); + +select is( + (select json_array_length(get_accounts())), + 1, + 'get_accounts returns 1 accounts (personal)' + ); + +select is( + (select get_personal_account() ->> 'account_id'), + auth.uid()::text, + 'get_personal_account should return the correct account_id' + ); + + +select throws_ok($$select create_account('my-account', 'My account')$$, + 'An account with that unique ID already exists'); + +select create_account('My AccOunt & 3'); + +select is( + (select (get_account_by_slug('my-account-3') ->> 'account_id')::uuid), + (select id from basejump.accounts where slug = 'my-account-3'), + 'get_account_by_slug returns the correct account_id' + ); + +select is( + (select json_array_length(get_accounts())), + 2, + 'get_accounts returns 2 accounts (personal and team)' + ); + + +-- Should not be able to update an account you aren't a member of + +select throws_ok( + $$select update_account('00000000-0000-0000-0000-000000000000', slug => 'my-account-new-slug')$$, + 'Not found' + ); + +-- Anon users should not have access to any of these functions + +select tests.clear_authentication(); + +select throws_ok( + $$select get_account('00000000-0000-0000-0000-000000000000')$$, + 'permission denied for function get_account' + ); + +select throws_ok( + $$select get_account_by_slug('my-account-3')$$, + 'permission denied for function get_account_by_slug' + ); + +select throws_ok( + $$select current_user_account_role('00000000-0000-0000-0000-000000000000')$$, + 'permission denied for function current_user_account_role' + ); + +select throws_ok( + $$select get_accounts()$$, + 'permission denied for function get_accounts' + ); + + +---- some functions should work for service_role users +select tests.authenticate_as_service_role(); + +select is( + (select (get_account('00000000-0000-0000-0000-000000000000') ->> 'account_id')::uuid), + '00000000-0000-0000-0000-000000000000'::uuid, + 'get_account should return the correct account_id' + ); + +select is( + (select (get_account_by_slug('my-updated-slug') ->> 'account_id')::uuid), + (select id from basejump.accounts where slug = 'my-updated-slug'), + 'get_account_by_slug returns the correct account_id' + ); + +select update_account('00000000-0000-0000-0000-000000000000', slug => 'my-updated-slug-300'); + +select is( + (select get_account('00000000-0000-0000-0000-000000000000') ->> 'slug'), + 'my-updated-slug-300', + 'Updating the account slug should work for service_role users' + ); + +SELECT * +FROM finish(); + +ROLLBACK; \ No newline at end of file diff --git a/supabase/tests/database/12-created-by-tracking.sql b/supabase/tests/database/12-created-by-tracking.sql new file mode 100644 index 0000000..26ee17b --- /dev/null +++ b/supabase/tests/database/12-created-by-tracking.sql @@ -0,0 +1,73 @@ +BEGIN; +create extension "basejump-supabase_test_helpers" + version '0.0.2'; + +select plan(5); + +-- make sure we're setup for enabling team accounts +update basejump.config +set enable_team_accounts = true; + +--- we insert a user into auth.users and return the id into user_id to use +select tests.create_supabase_user('test1'); +select tests.create_supabase_user('test_member'); + +------------ +--- Primary Owner +------------ +select tests.authenticate_as('test1'); + +insert into basejump.accounts (id, name, slug) +values ('00000000-0000-0000-0000-000000000000', 'test', 'test'); + +insert into basejump.accounts (id, name, slug) +values ('00000000-0000-0000-0000-000000000001', 'test', 'test2'); + +select is( + (select created_by from basejump.accounts where id = '00000000-0000-0000-0000-000000000000'), + tests.get_supabase_uid('test1'), + 'created_by is set to the user that created the account' + ); +select is( + (select updated_by from basejump.accounts where id = '00000000-0000-0000-0000-000000000000'), + tests.get_supabase_uid('test1'), + 'created_by is set to the user that created the account' + ); + +--- test updating accounts +select tests.clear_authentication(); +set role postgres; + +insert into basejump.account_user (account_id, account_role, user_id) +values ('00000000-0000-0000-0000-000000000000', 'owner', tests.get_supabase_uid('test_member')); + +update basejump.accounts +set name = 'test update' +where id = '00000000-0000-0000-0000-000000000001'; + +select is( + (select updated_by from basejump.accounts where id = '00000000-0000-0000-0000-000000000001'), + NULL, + 'Updtaes from postgres / service_role users set updated_by field to null' + ); + +select tests.authenticate_as('test_member'); + +select update_account('00000000-0000-0000-0000-000000000000', slug => 'updated-slug'); + +select is( + (select updated_by from basejump.accounts where id = '00000000-0000-0000-0000-000000000000'), + tests.get_supabase_uid('test_member'), + 'updated_by is set to the user that updated the account' + ); + +select is( + (select created_by from basejump.accounts where id = '00000000-0000-0000-0000-000000000000'), + tests.get_supabase_uid('test1'), + 'created_by is set to the user that created the account' + ); + +SELECT * +FROM finish(); + +ROLLBACK; \ No newline at end of file diff --git a/supabase/tests/database/14-public-billing-functions.sql b/supabase/tests/database/14-public-billing-functions.sql new file mode 100644 index 0000000..2dc9dab --- /dev/null +++ b/supabase/tests/database/14-public-billing-functions.sql @@ -0,0 +1,199 @@ +BEGIN; +create extension "basejump-supabase_test_helpers" + version '0.0.2'; + +update basejump.config +set enable_team_account_billing = TRUE, + enable_personal_account_billing = true; + +select plan(6); + +select tests.create_supabase_user('test1'); +select tests.create_supabase_user('non_member'); + +select tests.authenticate_as('test1'); + +insert into basejump.accounts (id, slug, name) +values ('00000000-0000-0000-0000-000000000000', 'my-known-account', 'My Known Account'); + +insert into basejump.accounts (id, slug, name) +values ('00000000-0000-0000-0000-000000000001', 'my-known-account-2', 'My Known Account 2'); + +select tests.authenticate_as_service_role(); + +select public.service_role_upsert_customer_subscription('00000000-0000-0000-0000-000000000000', '{ + "id": "cus_00000000000000", + "billing_email": "test@test.com", + "provider": "stripe" +}'::jsonb, '{ + "id": "sub_00000000000000", + "billing_customer_id": "cus_00000000000000", + "status": "active", + "metadata": "{\"foo\": \"bar\"}", + "price_id": "price_00000000000000", + "quantity": 1, + "cancel_at_period_end": false, + "created": "2021-06-10T00:00:00Z", + "current_period_start": "2023-06-10T00:00:00Z", + "current_period_end": "2023-07-10T00:00:00Z", + "ended_at": null, + "cancel_at": null, + "canceled_at": null, + "trial_start": null, + "trial_end": null, + "plan_name": "Plan Name", + "provider": "stripe" +}'::jsonb); + + +select public.service_role_upsert_customer_subscription('00000000-0000-0000-0000-000000000001', '{ + "id": "cus_00000000000001", + "billing_email": "test@test.com", + "provider": "stripe" +}'::jsonb, '{ + "id": "sub_00000000000001", + "billing_customer_id": "cus_00000000000001", + "status": "active", + "price_id": "price_00000000000000", + "quantity": 1, + "cancel_at_period_end": false, + "created": "2021-06-10T00:00:00Z", + "current_period_start": "2023-06-10T00:00:00Z", + "current_period_end": "2023-07-10T00:00:00Z", + "ended_at": null, + "cancel_at": null, + "canceled_at": null, + "trial_start": null, + "trial_end": null, + "plan_name": "Plan Name", + "provider": "stripe" +}'::jsonb); + + +select tests.authenticate_as('test1'); + +select row_eq( + $$ select get_account_billing_status('00000000-0000-0000-0000-000000000000') $$, + ROW ('{ + "account_role": "owner", + "is_primary_owner": true, + "is_personal_account": false, + "account_id": "00000000-0000-0000-0000-000000000000", + "billing_subscription_id": "sub_00000000000000", + "billing_status": "active", + "billing_customer_id": "cus_00000000000000", + "billing_provider": "stripe", + "billing_email": "test@test.com", + "billing_enabled": true + }'::jsonb), + 'get_account_billing_status should return the newly inserted data' + ); + +--- test out updating customer +select tests.authenticate_as_service_role(); + +select public.service_role_upsert_customer_subscription('00000000-0000-0000-0000-000000000000', customer => '{ + "id": "cus_00000000000000", + "billing_email": "test2@test.com", + "provider": "stripe" +}'::jsonb); + +select tests.authenticate_as('test1'); +select row_eq( + $$ select get_account_billing_status('00000000-0000-0000-0000-000000000000') $$, + ROW ('{ + "account_role": "owner", + "is_primary_owner": true, + "is_personal_account": false, + "account_id": "00000000-0000-0000-0000-000000000000", + "billing_subscription_id": "sub_00000000000000", + "billing_status": "active", + "billing_customer_id": "cus_00000000000000", + "billing_provider": "stripe", + "billing_email": "test2@test.com", + "billing_enabled": true + }'::jsonb), + 'get_account_billing_status should return the newly inserted data' + ); + + +select tests.authenticate_as_service_role(); + +select public.service_role_upsert_customer_subscription('00000000-0000-0000-0000-000000000000', subscription => '{ + "id": "sub_00000000000000", + "billing_customer_id": "cus_00000000000000", + "status": "canceled", + "price_id": "price_00000000000000", + "quantity": 1, + "cancel_at_period_end": false, + "created": "2021-06-10T00:00:00Z", + "current_period_start": "2023-06-10T00:00:00Z", + "current_period_end": "2023-07-10T00:00:00Z", + "ended_at": null, + "cancel_at": null, + "canceled_at": "2023-07-10T00:00:00Z", + "trial_start": null, + "trial_end": null, + "plan_name": "Plan Name", + "provider": "stripe" +}'::jsonb || jsonb_build_object('metadata', '{ + "foo2": "bar" +}'::jsonb)); + +select row_eq( + $$ select metadata, canceled_at, status from basejump.billing_subscriptions where id = 'sub_00000000000000' $$, + ROW ('{ + "foo2": "bar" + }'::jsonb, '2023-07-10T00:00:00Z'::timestamptz, 'canceled'::basejump.subscription_status), + 'metadata should be updated' + ); + +select tests.authenticate_as('test1'); +select row_eq( + $$ select get_account_billing_status('00000000-0000-0000-0000-000000000000') $$, + ROW ('{ + "account_role": "owner", + "is_primary_owner": true, + "is_personal_account": false, + "account_id": "00000000-0000-0000-0000-000000000000", + "billing_subscription_id": "sub_00000000000000", + "billing_status": "canceled", + "billing_customer_id": "cus_00000000000000", + "billing_provider": "stripe", + "billing_email": "test2@test.com", + "billing_enabled": true + }'::jsonb), + 'get_account_billing_status should return the newly inserted data' + ); + + +-- make sure nothing changes in the other one +select tests.authenticate_as('test1'); +select row_eq( + $$ select get_account_billing_status('00000000-0000-0000-0000-000000000001') $$, + ROW ('{ + "account_role": "owner", + "is_primary_owner": true, + "is_personal_account": false, + "account_id": "00000000-0000-0000-0000-000000000001", + "billing_subscription_id": "sub_00000000000001", + "billing_status": "active", + "billing_customer_id": "cus_00000000000001", + "billing_provider": "stripe", + "billing_email": "test@test.com", + "billing_enabled": true + }'::jsonb), + 'get_account_billing_status should return the newly inserted data' + ); + +select tests.authenticate_as('non_member'); + +SELECT throws_ok( + $$ select get_account_billing_status('00000000-0000-0000-0000-000000000001') $$, + 'P0001' + ); + +SELECT * +FROM finish(); + +ROLLBACK; \ No newline at end of file diff --git a/supabase/tests/database/15-billing-disabled-functions.sql b/supabase/tests/database/15-billing-disabled-functions.sql new file mode 100644 index 0000000..565b289 --- /dev/null +++ b/supabase/tests/database/15-billing-disabled-functions.sql @@ -0,0 +1,115 @@ +BEGIN; +create extension "basejump-supabase_test_helpers" + version '0.0.2'; + +update basejump.config +set enable_personal_account_billing = FALSE, + enable_team_account_billing = FALSE; + +select plan(6); + + +select tests.create_supabase_user('test1', 'test@test.com'); +select tests.authenticate_as('test1'); + +insert into basejump.accounts (id, slug, name) +values ('00000000-0000-0000-0000-000000000000', 'my-known-account', 'My Known Account'); + +insert into basejump.accounts (id, slug, name) +values ('00000000-0000-0000-0000-000000000001', 'my-known-account-2', 'My Known Account 2'); + +select tests.authenticate_as('test1'); + +select row_eq( + $$ select get_account_billing_status('00000000-0000-0000-0000-000000000000') $$, + ROW ('{ + "account_id": "00000000-0000-0000-0000-000000000000", + "account_role": "owner", + "billing_email": "test@test.com", + "billing_status": null, + "billing_enabled": false, + "billing_provider": "stripe", + "is_primary_owner": true, + "billing_customer_id": null, + "is_personal_account": false, + "billing_subscription_id": null + }'::jsonb), + 'get_account_billing_status should handle disabled billing correctly' + ); + +select row_eq( + $$ select (get_account_billing_status((get_personal_account() ->> 'account_id')::uuid) ->> 'billing_enabled')::boolean $$, + ROW (FALSE), + 'get_account_billing_status should handle disabled billing correctly' + ); + +-- try enabling personal account billing but not team +select tests.clear_authentication(); +set role postgres; + +update basejump.config +set enable_personal_account_billing = TRUE, + enable_team_account_billing = FALSE; + +select tests.authenticate_as('test1'); + +select row_eq( + $$ select get_account_billing_status('00000000-0000-0000-0000-000000000000') $$, + ROW ('{ + "account_id": "00000000-0000-0000-0000-000000000000", + "account_role": "owner", + "billing_email": "test@test.com", + "billing_status": null, + "billing_enabled": false, + "billing_provider": "stripe", + "is_primary_owner": true, + "billing_customer_id": null, + "is_personal_account": false, + "billing_subscription_id": null + }'::jsonb), + 'get_account_billing_status should handle disabled billing correctly' + ); + +select row_eq( + $$ select (get_account_billing_status((get_personal_account() ->> 'account_id')::uuid) ->> 'billing_enabled')::boolean $$, + ROW (TRUE), + 'get_account_billing_status should handle enabled personal account billing correctly' + ); + +--- enable team account billing but not personal +select tests.clear_authentication(); +set role postgres; + +update basejump.config +set enable_personal_account_billing = FALSE, + enable_team_account_billing = TRUE; + +select tests.authenticate_as('test1'); + +select row_eq( + $$ select get_account_billing_status('00000000-0000-0000-0000-000000000000') $$, + ROW ('{ + "account_id": "00000000-0000-0000-0000-000000000000", + "account_role": "owner", + "billing_email": "test@test.com", + "billing_status": null, + "billing_enabled": true, + "billing_provider": "stripe", + "is_primary_owner": true, + "billing_customer_id": null, + "is_personal_account": false, + "billing_subscription_id": null + }'::jsonb), + 'get_account_billing_status should handle enabled team billing correctly' + ); + +select row_eq( + $$ select (get_account_billing_status((get_personal_account() ->> 'account_id')::uuid) ->> 'billing_enabled')::boolean $$, + ROW (FALSE), + 'get_account_billing_status should handle disabled billing correctly' + ); + +SELECT * +FROM finish(); + +ROLLBACK; \ No newline at end of file diff --git a/supabase/tests/database/3-personal-accounts-disabled.sql b/supabase/tests/database/3-personal-accounts-disabled.sql deleted file mode 100644 index cd60cf6..0000000 --- a/supabase/tests/database/3-personal-accounts-disabled.sql +++ /dev/null @@ -1,23 +0,0 @@ -BEGIN; -CREATE EXTENSION "basejump-supabase_test_helpers"; - -select plan(1); - --- make sure we're setup for enabling personal accounts -update basejump.config -set enable_personal_accounts = false; - ---- we insert a user into auth.users and return the id into user_id to use -select tests.create_supabase_user('test1'); - - --- should create the personal account automatically -SELECT is_empty( - $$ select * from accounts $$, - 'No personal account should be created when personal acounts are disabled' - ); - -SELECT * -FROM finish(); - -ROLLBACK; \ No newline at end of file diff --git a/supabase/tests/database/9-profiles.sql b/supabase/tests/database/9-profiles.sql deleted file mode 100644 index 727c6fc..0000000 --- a/supabase/tests/database/9-profiles.sql +++ /dev/null @@ -1,73 +0,0 @@ -BEGIN; -CREATE EXTENSION "basejump-supabase_test_helpers"; - -select plan(6); --- make sure we're setup for enabling personal accounts -update basejump.config -set enable_team_accounts = true; - --- setup the users we need for testing -select tests.create_supabase_user('test1', 'test@test.com'); -select tests.create_supabase_user('test2', 'test2@test.com'); - - ---- start acting as an authenticated user -select tests.authenticate_as('test1'); - --- user should have access to their own profile -select is( - (select name from profiles where id = tests.get_supabase_uid('test1')), - 'test', - 'User should have access to their own profile, profile should auto-set name to first half of email' - ); - --- user should not have access to other profiles -select is_empty( - $$ select * from profiles where id <> tests.get_supabase_uid('test1') $$, - 'User should not have access to any other profiles' - ); - --- Users should be able to update their own names -select row_eq( - $$ update profiles set name = 'test update' where id = tests.get_supabase_uid('test1') returning name $$, - ROW ('test update'::text), - 'User should be able to update their own name' - ); - --- User should not be able to update other users names -select results_ne( - $$ update profiles set name = 'test update' where id = tests.get_supabase_uid('test2') returning 1 $$, - $$ values(1) $$, - 'Should not be able to update profile' - ); - --- Create a new account so you can start sharing profiles -insert into accounts (id, team_name, personal_account) -values ('eb3a0306-7331-4c42-a580-970e7ba6a11d', 'test team', false); - --- set role to postgres, and then insert an account_user for the second user -select tests.clear_authentication(); -set local role postgres; -insert into account_user (account_id, user_id, account_role) -values ('eb3a0306-7331-4c42-a580-970e7ba6a11d', tests.get_supabase_uid('test2'), 'owner'); - --- back to authenticated user -select tests.authenticate_as('test1'); - --- User should now have access to the second profile -select row_eq( - $$ select name from profiles where id = tests.get_supabase_uid('test2') $$, - ROW ('test2'::text), - 'User should have access to teammates profiles' - ); - --- still can't update teammates profiles -select results_ne( - $$ update profiles set name = 'test update' where id = tests.get_supabase_uid('test2') returning 1 $$, - $$ values(1) $$, - 'Should not be able to update profile' - ); -SELECT * -FROM finish(); - -ROLLBACK; \ No newline at end of file diff --git a/supabase/tests/integration/.gitignore b/supabase/tests/integration/.gitignore new file mode 100644 index 0000000..68c5d18 --- /dev/null +++ b/supabase/tests/integration/.gitignore @@ -0,0 +1,5 @@ +node_modules/ +/test-results/ +/playwright-report/ +/blob-report/ +/playwright/.cache/ diff --git a/supabase/tests/integration/invalid-requests.spec.ts b/supabase/tests/integration/invalid-requests.spec.ts new file mode 100644 index 0000000..8deba18 --- /dev/null +++ b/supabase/tests/integration/invalid-requests.spec.ts @@ -0,0 +1,59 @@ +import {expect, test} from '@playwright/test'; +import testInvalidNewSubscriptionUrl from "./utils/test-invalid-new-subscription-url.ts"; +import testInvalidBillingPortalUrl from "./utils/test-invalid-billing-portal-url.ts"; +import testMissingAccountId from "./utils/test-missing-account-id.ts"; +import setupBasejumpAccount from "./utils/setup-basejump-account.ts"; +import testUnauthorized from "./utils/test-unauthorized.ts"; + +const timestamp = Date.now(); +const uniqueIdentifier = `invalid-requests-${timestamp}`; + +test('Shouldnt be able to make things up or pass bad data', async ({page}) => { + const {accountId, supabaseClient} = await setupBasejumpAccount(uniqueIdentifier); + + + /*** + * Initialize a subscription with the default plan and trial days (ENV var configured) + */ + + await testInvalidNewSubscriptionUrl(supabaseClient, accountId); + + /*** + * Get billing portal without a valid return URL + */ + + await testInvalidBillingPortalUrl(supabaseClient, accountId); + + + /*** + * Hit all endpoints without an account ID + */ + + await testMissingAccountId(supabaseClient); + + + /*** + * Hit an invalid endpoint action + */ + + const {error} = await supabaseClient.functions.invoke('test-stripe-billing-functions', { + body: { + action: 'not_a_thing', + args: { + account_id: "account_id_here" + } + } + }); + + const invalidUrlError = await error.context.json(); + + expect(invalidUrlError.error).toEqual('Invalid action'); + + /** + * Unauthorized requests + */ + + const {supabaseClient: unauthorizedClient} = await setupBasejumpAccount(`unauthorized-access-${timestamp}`); + + await testUnauthorized(unauthorizedClient, accountId); +}); \ No newline at end of file diff --git a/supabase/tests/integration/package.json b/supabase/tests/integration/package.json new file mode 100644 index 0000000..2ba67b9 --- /dev/null +++ b/supabase/tests/integration/package.json @@ -0,0 +1,20 @@ +{ + "name": "functions", + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "devDependencies": { + "@playwright/test": "^1.40.0", + "@types/node": "^20.10.0" + }, + "scripts": { + "test:setup": "playwright install --with-deps", + "test:invalid": "playwright test invalid-requests.spec.ts", + "test:stripe-no-webhooks": "playwright test stripe-full-no-webhooks.spec.ts", + "test:stripe-webhooks": "playwright test stripe-full-only-webhooks.spec.ts" + }, + "dependencies": { + "@supabase/supabase-js": "^2.38.5", + "stripe": "^14.7.0" + } +} diff --git a/supabase/tests/integration/playwright.config.ts b/supabase/tests/integration/playwright.config.ts new file mode 100644 index 0000000..2c8ddb1 --- /dev/null +++ b/supabase/tests/integration/playwright.config.ts @@ -0,0 +1,77 @@ +import {defineConfig, devices} from '@playwright/test'; + +/** + * Read environment variables from file. + * https://github.com/motdotla/dotenv + */ +// require('dotenv').config(); + +/** + * See https://playwright.dev/docs/test-configuration. + */ +export default defineConfig({ + testDir: './.', + /* Run tests in files in parallel */ + fullyParallel: true, + /* Fail the build on CI if you accidentally left test.only in the source code. */ + forbidOnly: !!process.env.CI, + /* Retry on CI only */ + retries: process.env.CI ? 2 : 0, + /* Opt out of parallel tests on CI. */ + workers: process.env.CI ? 1 : undefined, + /* Reporter to use. See https://playwright.dev/docs/test-reporters */ + reporter: 'html', + /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ + use: { + /* Base URL to use in actions like `await page.goto('/')`. */ + // baseURL: 'http://127.0.0.1:3000', + + /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ + trace: 'on-first-retry', + }, + + /* Configure projects for major browsers */ + projects: [ + { + name: 'chromium', + use: {...devices['Desktop Chrome']}, + }, + + // { + // name: 'firefox', + // use: { ...devices['Desktop Firefox'] }, + // }, + // + // { + // name: 'webkit', + // use: { ...devices['Desktop Safari'] }, + // }, + + /* Test against mobile viewports. */ + // { + // name: 'Mobile Chrome', + // use: { ...devices['Pixel 5'] }, + // }, + // { + // name: 'Mobile Safari', + // use: { ...devices['iPhone 12'] }, + // }, + + /* Test against branded browsers. */ + // { + // name: 'Microsoft Edge', + // use: { ...devices['Desktop Edge'], channel: 'msedge' }, + // }, + // { + // name: 'Google Chrome', + // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, + // }, + ], + + /* Run your local dev server before starting the tests */ + // webServer: { + // command: 'npm run start', + // url: 'http://127.0.0.1:3000', + // reuseExistingServer: !process.env.CI, + // }, +}); diff --git a/supabase/tests/integration/stripe-full-no-webhooks.spec.ts b/supabase/tests/integration/stripe-full-no-webhooks.spec.ts new file mode 100644 index 0000000..a9dcce6 --- /dev/null +++ b/supabase/tests/integration/stripe-full-no-webhooks.spec.ts @@ -0,0 +1,134 @@ +import {expect, test} from '@playwright/test'; +import getVerifiedNewSubscriptionUrl from "./utils/get-verified-new-subscription-url.ts"; +import getVerifiedAccountStatus from "./utils/get-verified-account-status.ts"; +import { + cancelStripeSubscription, + playwrightCancelStripeCheckout, + playwrightFillInStripeCard, + playwrightUpdatePaymentBillingPortal, + removeStripeSubscriptionTrial +} from "./utils/stripe-actions.ts"; +import getVerifiedBillingPortalUrl from "./utils/get-verified-billing-portal-url.ts"; +import { + BILLING_PORTAL_RETURN_URL, + NEW_SUBSCRIPTION_CANCEL_URL, + NEW_SUBSCRIPTION_SUCCESS_URL +} from "./utils/variables.ts"; +import setupBasejumpAccount from "./utils/setup-basejump-account.ts"; +import Stripe from "stripe"; + +const timestamp = Date.now(); +const uniqueIdentifier = `stripe-no-webhooks-${timestamp}` +const stripeClient = new Stripe(process.env.STRIPE_API_KEY); + +test('Should be able to sign up, register for a trial, convert to an account, fail next payment and be deactivated, then fix', async ({page}) => { + const {accountId, supabaseClient} = await setupBasejumpAccount(uniqueIdentifier); + + /*** + * Get billing status and make sure we're not active + */ + await getVerifiedAccountStatus(supabaseClient, accountId, { + subscriptionActive: false, + billingEnabled: true, + subscriptionStatus: "not_setup" + }); + + /*** + * Check available plans + */ + const {data: plans} = await supabaseClient.functions.invoke('test-stripe-billing-functions', { + body: { + action: 'get_plans', + args: { + account_id: accountId + } + } + }); + + expect(plans.length).toBeGreaterThan(0); + + /*** + * Initialize a subscription with the default plan and trial days (ENV var configured) + */ + + const newSubscriptionData = await getVerifiedNewSubscriptionUrl(supabaseClient, accountId); + + // go to stripe url and complete checkout + await page.goto(newSubscriptionData.url); + + await playwrightCancelStripeCheckout(page); + + await page.waitForURL(NEW_SUBSCRIPTION_CANCEL_URL, {timeout: 10000}); + + expect(page.url()).toEqual(NEW_SUBSCRIPTION_CANCEL_URL); + + await page.goto(newSubscriptionData.url); + + await playwrightFillInStripeCard(page, 'declined'); + + await page.waitForURL(NEW_SUBSCRIPTION_SUCCESS_URL, {timeout: 10000}); + + expect(page.url()).toEqual(NEW_SUBSCRIPTION_SUCCESS_URL); + + /*** + * Verify subscription is now active and in trial state + */ + + + + const trialData = await getVerifiedAccountStatus(supabaseClient, accountId, { + subscriptionActive: true, + billingEnabled: true, + subscriptionStatus: "trialing" + }); + + /*** + * Convert trial to plan, will fail since bad card + */ + + await removeStripeSubscriptionTrial(stripeClient, trialData.subscription_id); + + await getVerifiedAccountStatus(supabaseClient, accountId, { + subscriptionActive: false, + billingEnabled: true, + subscriptionStatus: "past_due" + }); + + + /*** + * Generate stripe URL to collect payment method + */ + + const billingPortal = await getVerifiedBillingPortalUrl(supabaseClient, accountId); + + await page.goto(billingPortal.url); + await playwrightUpdatePaymentBillingPortal(page, 'valid'); + + await page.waitForURL(BILLING_PORTAL_RETURN_URL, {timeout: 10000}); + + expect(page.url()).toEqual(BILLING_PORTAL_RETURN_URL); + + + /*** + * Verify subscription is now active + */ + + // delay to allow stripe to process payments + await getVerifiedAccountStatus(supabaseClient, accountId, { + subscriptionActive: true, + billingEnabled: true, + subscriptionStatus: "active" + }); + + /*** + * Cancel subscription + */ + + await cancelStripeSubscription(stripeClient, trialData.subscription_id); + + await getVerifiedAccountStatus(supabaseClient, accountId, { + subscriptionActive: false, + billingEnabled: true, + subscriptionStatus: "canceled" + }); +}); \ No newline at end of file diff --git a/supabase/tests/integration/stripe-full-only-webhooks.spec.ts b/supabase/tests/integration/stripe-full-only-webhooks.spec.ts new file mode 100644 index 0000000..71acb0a --- /dev/null +++ b/supabase/tests/integration/stripe-full-only-webhooks.spec.ts @@ -0,0 +1,118 @@ +import {expect, test} from '@playwright/test'; +import setupBasejumpAccount from "./utils/setup-basejump-account.ts"; +import { + cancelStripeSubscription, + createStripeCustomer, + newStripeSubscriptionUrl, + playwrightFillInStripeCard, + playwrightUpdatePaymentBillingPortal, + removeStripeSubscriptionTrial, + stripeBillingPortalUrl +} from "./utils/stripe-actions.ts"; +import Stripe from "stripe"; + +const timestamp = Date.now(); +const uniqueIdentifier = `stripe-only-webhooks-${timestamp}` +const stripeClient = new Stripe(process.env.STRIPE_API_KEY); + +const stripeDefaultPlanId = process.env.STRIPE_DEFAULT_PLAN_ID; + +test('Should be able to perform full user journey using only webhook updates from Stripe', async ({page}) => { + /** + * Repeat full stripe flow, but ONLY by interacting with Stripe directly and then querying Supabase for the results + * after the webhook has hit + */ + + const {accountId, supabaseClient, billingEmail} = await setupBasejumpAccount(uniqueIdentifier); + + /** + * Create a new Stripe customer + */ + + const customerId = await createStripeCustomer(stripeClient, billingEmail, accountId); + await expect.poll(async () => { + const {data} = await supabaseClient.rpc('get_account_billing_status', {account_id: accountId}); + return data.billing_customer_id; + }, { + // Custom error message, optional. + message: 'customer ID should be returned', // custom error message + // Poll for 10 seconds; defaults to 5 seconds. Pass 0 to disable timeout. + timeout: 10000, + }).toEqual(customerId); + + /** + * Create a new stripe subscription, complete it, and make sure we get back the trial + */ + + const subscriptionUrl = await newStripeSubscriptionUrl(stripeClient, customerId, accountId, stripeDefaultPlanId); + + await page.goto(subscriptionUrl); + + await playwrightFillInStripeCard(page, 'declined'); + + await expect.poll(async () => { + const {data} = await supabaseClient.rpc('get_account_billing_status', {account_id: accountId}); + return data.billing_status; + }, { + // Custom error message, optional. + message: 'Account should be trialing', // custom error message + // Poll for 10 seconds; defaults to 5 seconds. Pass 0 to disable timeout. + timeout: 10000, + }).toEqual('trialing'); + + + const {data: trialingData} = await supabaseClient.rpc('get_account_billing_status', {account_id: accountId}); + const subscriptionId = trialingData.billing_subscription_id; + + expect(trialingData.billing_subscription_id).not.toBeNull(); + expect(trialingData.billing_provider).toEqual('stripe'); + expect(trialingData.billing_email).toEqual(billingEmail); + + /** + * Cancel trial, should go past_due + */ + + await removeStripeSubscriptionTrial(stripeClient, subscriptionId); + + await expect.poll(async () => { + const {data} = await supabaseClient.rpc('get_account_billing_status', {account_id: accountId}); + return data.billing_status; + }, { + // Custom error message, optional. + message: 'Account should be delinquent after first payment', // custom error message + // Poll for 10 seconds; defaults to 5 seconds. Pass 0 to disable timeout. + timeout: 10000, + }).toEqual('past_due'); + + /** + * Fix subscription through billing portal URL + */ + + const billingPortalUrl = await stripeBillingPortalUrl(stripeClient, customerId); + + await page.goto(billingPortalUrl); + + await playwrightUpdatePaymentBillingPortal(page, 'valid'); + + await expect.poll(async () => { + const {data} = await supabaseClient.rpc('get_account_billing_status', {account_id: accountId}); + return data.billing_status; + }, { + // Custom error message, optional. + message: 'Account should be active after fixing', // custom error message + // Poll for 10 seconds; defaults to 5 seconds. Pass 0 to disable timeout. + timeout: 10000, + }).toEqual('active'); + + await cancelStripeSubscription(stripeClient, subscriptionId); + + await expect.poll(async () => { + const {data} = await supabaseClient.rpc('get_account_billing_status', {account_id: accountId}); + return data.billing_status; + }, { + // Custom error message, optional. + message: 'Account should be canceled after customer cancels', // custom error message + // Poll for 10 seconds; defaults to 5 seconds. Pass 0 to disable timeout. + timeout: 10000, + }).toEqual('canceled'); +}); \ No newline at end of file diff --git a/supabase/tests/integration/utils/get-verified-account-status.ts b/supabase/tests/integration/utils/get-verified-account-status.ts new file mode 100644 index 0000000..8d74de8 --- /dev/null +++ b/supabase/tests/integration/utils/get-verified-account-status.ts @@ -0,0 +1,22 @@ +import {expect} from "@playwright/test"; + +export default async function getVerifiedAccountStatus(supabaseClient, accountId, { + subscriptionActive = false, + billingEnabled = true, + subscriptionStatus = 'trialing' +}) { + const {data} = await supabaseClient.functions.invoke('test-stripe-billing-functions', { + body: { + action: 'get_billing_status', + args: { + account_id: accountId + } + } + }); + + expect(data.subscription_active).toEqual(subscriptionActive); + expect(data.billing_enabled).toEqual(billingEnabled); + expect(data.status).toEqual(subscriptionStatus); + + return data; +} \ No newline at end of file diff --git a/supabase/tests/integration/utils/get-verified-billing-portal-url.ts b/supabase/tests/integration/utils/get-verified-billing-portal-url.ts new file mode 100644 index 0000000..282ce84 --- /dev/null +++ b/supabase/tests/integration/utils/get-verified-billing-portal-url.ts @@ -0,0 +1,19 @@ +import {expect} from "@playwright/test"; +import {BILLING_PORTAL_RETURN_URL} from "./variables.ts"; + +export default async function getVerifiedBillingPortalUrl(supabaseClient, accountId) { + const {data, error} = await supabaseClient.functions.invoke('test-stripe-billing-functions', { + body: { + action: "get_billing_portal_url", + args: { + account_id: accountId, + return_url: BILLING_PORTAL_RETURN_URL, + } + } + }); + + expect(data.billing_enabled).toEqual(true); + expect(data.url).not.toBeNull(); + + return data; +} \ No newline at end of file diff --git a/supabase/tests/integration/utils/get-verified-new-subscription-url.ts b/supabase/tests/integration/utils/get-verified-new-subscription-url.ts new file mode 100644 index 0000000..b9bc012 --- /dev/null +++ b/supabase/tests/integration/utils/get-verified-new-subscription-url.ts @@ -0,0 +1,21 @@ +import {expect} from "@playwright/test"; +import {NEW_SUBSCRIPTION_CANCEL_URL, NEW_SUBSCRIPTION_SUCCESS_URL} from "./variables.ts"; + +export default async function getVerifiedNewSubscriptionUrl(supabaseClient, accountId, planId = null) { + const {data} = await supabaseClient.functions.invoke('test-stripe-billing-functions', { + body: { + action: "get_new_subscription_url", + args: { + account_id: accountId, + plan_id: planId, + success_url: NEW_SUBSCRIPTION_SUCCESS_URL, + cancel_url: NEW_SUBSCRIPTION_CANCEL_URL, + } + } + }); + + expect(data.billing_enabled).toEqual(true); + expect(data.url).not.toBeNull(); + + return data; +} \ No newline at end of file diff --git a/supabase/tests/integration/utils/setup-basejump-account.ts b/supabase/tests/integration/utils/setup-basejump-account.ts new file mode 100644 index 0000000..2cac55a --- /dev/null +++ b/supabase/tests/integration/utils/setup-basejump-account.ts @@ -0,0 +1,29 @@ +import {expect} from "@playwright/test"; +import {createClient, SupabaseClient} from "@supabase/supabase-js"; + +export default async function setupBasejumpAccount(uniqueIdentifier: string): { + supabaseClient: SupabaseClient, + accountId: string, + billingEmail: string, +} { + const supabaseClient = createClient(process.env.SUPABASE_URL, process.env.SUPABASE_ANON_KEY); + const billingEmail = `test+${uniqueIdentifier}@test.com`; + + const {data: {session}} = await supabaseClient.auth.signUp({ + email: billingEmail, + password: 'test1234', + }); + + expect(session.access_token).not.toBeNull(); + + const {data: account} = await supabaseClient.rpc('create_account', { + slug: uniqueIdentifier, + name: uniqueIdentifier + }); + + expect(account.slug).toEqual(uniqueIdentifier); + + const accountId = account.account_id; + + return {supabaseClient, accountId, billingEmail}; +} \ No newline at end of file diff --git a/supabase/tests/integration/utils/stripe-actions.ts b/supabase/tests/integration/utils/stripe-actions.ts new file mode 100644 index 0000000..9dff5be --- /dev/null +++ b/supabase/tests/integration/utils/stripe-actions.ts @@ -0,0 +1,156 @@ +import {BILLING_PORTAL_RETURN_URL, NEW_SUBSCRIPTION_CANCEL_URL, NEW_SUBSCRIPTION_SUCCESS_URL} from "./variables.ts"; + +const stripeCards = { + valid: { + number: '4242 4242 4242 4242', + expiry: '04 / 24', + cvc: '424', + name: 'Tester', + zip: '777777', + }, + invalid: { + number: '4000 0000 0000 0002', + expiry: '04 / 24', + cvc: '424', + name: 'Tester', + zip: '777777', + }, + declined: { + number: '4000 0000 0000 0341', + expiry: '04 / 24', + cvc: '424', + name: 'Tester', + zip: '777777', + } +} + +/** + * For a given page, fill in stripe card details on a payment form using Playwright + * @param page + * @param cardType + */ +export async function playwrightFillInStripeCard(page, cardType: 'valid' | 'invalid' | 'declined' = 'valid') { + await page.getByPlaceholder('1234 1234 1234').fill(stripeCards[cardType].number); + await page.getByPlaceholder('MM / YY').fill(stripeCards[cardType].expiry); + await page.getByPlaceholder('CVC').fill(stripeCards[cardType].cvc); + await page.getByPlaceholder('Full name on card').fill(stripeCards[cardType].name); + await page.getByPlaceholder('ZIP').fill(stripeCards[cardType].zip); + await page.getByTestId('hosted-payment-submit-button').click(); +} + +/** + * For a given page, update a card on the stripe billing portal using Playwright + * @param page + * @param cardType + */ +export async function playwrightUpdatePaymentBillingPortal(page, cardType: 'valid' | 'invalid' | 'declined' = 'valid') { + await page.locator('[data-test="pay-subscription-open-invoice"]').click(); + await page.getByText('Add payment method').click(); + await page.frameLocator('iframe[name^="__privateStripeFrame"]').first().getByPlaceholder('1234 1234 1234').click(); + await page.frameLocator('iframe[name^="__privateStripeFrame"]').first().getByPlaceholder('1234 1234 1234').fill(stripeCards[cardType].number); + await page.frameLocator('iframe[name^="__privateStripeFrame"]').first().getByPlaceholder('MM / YY').fill(stripeCards[cardType].expiry); + await page.frameLocator('iframe[name^="__privateStripeFrame"]').first().getByPlaceholder('CVC').fill(stripeCards[cardType].cvc); + await page.frameLocator('iframe[name^="__privateStripeFrame"]').first().getByPlaceholder('12345').fill(stripeCards[cardType].zip); + await page.getByTestId('confirm').click(); + // wait for the words Invoice History to show on the page + await page.waitForSelector('text=Invoice History'); + await page.getByRole('link', {name: 'Basejump Test mode'}).click(); +} + +/** + * For a given page on the stripe checkoug, cancel and go back to Basejump cancel URL + * @param page + */ +export async function playwrightCancelStripeCheckout(page) { + await page.getByLabel('Back to Basejump').click(); +} + +/** + * Cancel the trial on a stripe account directly in stripe + * @param stripeClient + * @param subscriptionId + */ +export async function removeStripeSubscriptionTrial(stripeClient, subscriptionId) { + const subscription = await stripeClient.subscriptions.update(subscriptionId, { + trial_end: 'now', + }); + return subscription; +} + +/** + * Cancel a subscription on a stripe account directly in stripe + * @param stripeClient + * @param subscriptionId + */ +export async function cancelStripeSubscription(stripeClient, subscriptionId) { + const subscription = await stripeClient.subscriptions.cancel(subscriptionId); + return subscription; +} + +/** + * Create a new customer directly in Stripe and return the customer ID + * @param stripeClient + * @param email + * @param basejumpAccountId + */ +export async function createStripeCustomer(stripeClient, email, basejumpAccountId) { + const customer = await stripeClient.customers.create({ + email, + metadata: { + basejump_account_id: basejumpAccountId, + }, + }); + + return customer.id; +} + +/** + * Create a new subscription checkout session directly in Stripe and return the URL to it + * @param stripeClient + * @param accountId + * @param planId + * @param trialDays + */ +export async function newStripeSubscriptionUrl(stripeClient, customerId, basejumpAccountId, planId, trialDays = 7) { + const trialEnd = Math.floor(Date.now() / 1000) + (trialDays * 24 * 60 * 60); + const session = await stripeClient.checkout.sessions.create({ + customer: customerId, + payment_method_types: ['card'], + subscription_data: { + trial_end: trialEnd, + trial_settings: { + end_behavior: { + missing_payment_method: 'create_invoice' // subscription will go past_due if no payment method is added in time + } + }, + metadata: { + basejump_account_id: basejumpAccountId, + }, + items: [ + { + plan: planId + }, + ], + }, + mode: "subscription", + success_url: NEW_SUBSCRIPTION_SUCCESS_URL, + cancel_url: NEW_SUBSCRIPTION_CANCEL_URL, + metadata: { + basejump_account_id: basejumpAccountId, + }, + }); + return session.url; +} + +/** + * Create a new billing portal session directly in Stripe and return the URL to it + * @param stripeClient + * @param customerId + */ +export async function stripeBillingPortalUrl(stripeClient, customerId) { + const session = await stripeClient.billingPortal.sessions.create({ + customer: customerId, + return_url: BILLING_PORTAL_RETURN_URL, + }); + return session.url; +} \ No newline at end of file diff --git a/supabase/tests/integration/utils/test-invalid-billing-portal-url.ts b/supabase/tests/integration/utils/test-invalid-billing-portal-url.ts new file mode 100644 index 0000000..5f8f98f --- /dev/null +++ b/supabase/tests/integration/utils/test-invalid-billing-portal-url.ts @@ -0,0 +1,28 @@ +import {expect} from "@playwright/test"; + +export default async function testInvalidBillingPortalUrl(supabaseClient, accountId) { + + const payloads = [ + { + account_id: accountId, + return_url: 'http://invalid:3000', + }, + { + account_id: accountId + } + ] + + for (const payload of payloads) { + const {error} = await supabaseClient.functions.invoke('test-stripe-billing-functions', { + body: { + action: "get_billing_portal_url", + args: payload + } + }); + + const invalidUrlError = await error.context.json() + + expect(invalidUrlError.error).toEqual('Return url is not allowed'); + } + +} \ No newline at end of file diff --git a/supabase/tests/integration/utils/test-invalid-new-subscription-url.ts b/supabase/tests/integration/utils/test-invalid-new-subscription-url.ts new file mode 100644 index 0000000..01b7c78 --- /dev/null +++ b/supabase/tests/integration/utils/test-invalid-new-subscription-url.ts @@ -0,0 +1,39 @@ +import {expect} from "@playwright/test"; + +export default async function testInvalidNewSubscriptionUrl(supabaseClient, accountId) { + + const payloads = [ + { + account_id: accountId, + success_url: 'http://invalid:3000', + cancel_url: 'http://127.0.0.1:54323', + }, + { + account_id: accountId, + success_url: 'http://127.0.0.1:54323', + cancel_url: 'http://invalid:3000', + }, + { + account_id: accountId, + cancel_url: 'http://127.0.0.1:54323', + }, + { + account_id: accountId, + success_url: 'http://127.0.0.1:54323', + } + ] + + for (const payload of payloads) { + const {error} = await supabaseClient.functions.invoke('test-stripe-billing-functions', { + body: { + action: "get_new_subscription_url", + args: payload + } + }); + + const invalidUrlError = await error.context.json() + + expect(invalidUrlError.error).toEqual('Success or cancel url is not allowed'); + } + +} \ No newline at end of file diff --git a/supabase/tests/integration/utils/test-missing-account-id.ts b/supabase/tests/integration/utils/test-missing-account-id.ts new file mode 100644 index 0000000..0f87583 --- /dev/null +++ b/supabase/tests/integration/utils/test-missing-account-id.ts @@ -0,0 +1,22 @@ +import {expect} from "@playwright/test"; + +export default async function testMissingAccountId(supabaseClient) { + const actions = [ + "get_new_subscription_url", + "get_billing_status", + "get_billing_portal_url", + ]; + + for (const action of actions) { + const {error} = await supabaseClient.functions.invoke('test-stripe-billing-functions', { + body: { + action, + args: {} + } + }); + + const invalidUrlError = await error.context.json(); + + expect(invalidUrlError.error).toEqual('Account id is required'); + } +} \ No newline at end of file diff --git a/supabase/tests/integration/utils/test-unauthorized.ts b/supabase/tests/integration/utils/test-unauthorized.ts new file mode 100644 index 0000000..71797f5 --- /dev/null +++ b/supabase/tests/integration/utils/test-unauthorized.ts @@ -0,0 +1,41 @@ +import {expect} from "@playwright/test"; +import {BILLING_PORTAL_RETURN_URL, NEW_SUBSCRIPTION_CANCEL_URL, NEW_SUBSCRIPTION_SUCCESS_URL} from "./variables.ts"; + +export default async function testUnauthorized(unauthorizedClient, accountId) { + + const payloads = [ + { + action: 'get_billing_portal_url', + args: { + account_id: accountId, + return_url: BILLING_PORTAL_RETURN_URL + } + }, + { + action: 'get_new_subscription_url', + args: { + account_id: accountId, + success_url: NEW_SUBSCRIPTION_SUCCESS_URL, + cancel_url: NEW_SUBSCRIPTION_CANCEL_URL + } + }, + { + action: 'get_billing_status', + args: { + account_id: accountId + } + } + ]; + + for (const payload of payloads) { + const {error} = await unauthorizedClient.functions.invoke('test-stripe-billing-functions', { + body: payload + }); + + const invalidUrlError = await error.context.json() + + expect(error.context.status).toEqual(401); + expect(invalidUrlError.error).toEqual('Unauthorized'); + } + +} \ No newline at end of file diff --git a/supabase/tests/integration/utils/variables.ts b/supabase/tests/integration/utils/variables.ts new file mode 100644 index 0000000..b7a0fdf --- /dev/null +++ b/supabase/tests/integration/utils/variables.ts @@ -0,0 +1,3 @@ +export const BILLING_PORTAL_RETURN_URL = 'http://127.0.0.1:54323/return'; +export const NEW_SUBSCRIPTION_SUCCESS_URL = 'http://127.0.0.1:54323/success'; +export const NEW_SUBSCRIPTION_CANCEL_URL = 'http://127.0.0.1:54323/cancel'; \ No newline at end of file diff --git a/supabase/tests/integration/yarn.lock b/supabase/tests/integration/yarn.lock new file mode 100644 index 0000000..ffdf821 --- /dev/null +++ b/supabase/tests/integration/yarn.lock @@ -0,0 +1,354 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@playwright/test@^1.40.0": + version "1.40.0" + resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.40.0.tgz#d06c506977dd7863aa16e07f2136351ecc1be6ed" + integrity sha512-PdW+kn4eV99iP5gxWNSDQCbhMaDVej+RXL5xr6t04nbKLCBwYtA046t7ofoczHOm8u6c+45hpDKQVZqtqwkeQg== + dependencies: + playwright "1.40.0" + +"@supabase/functions-js@^2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@supabase/functions-js/-/functions-js-2.1.5.tgz#ed1b85f499dfda21d40fe39b86ab923117cb572b" + integrity sha512-BNzC5XhCzzCaggJ8s53DP+WeHHGT/NfTsx2wUSSGKR2/ikLFQTBCDzMvGz/PxYMqRko/LwncQtKXGOYp1PkPaw== + dependencies: + "@supabase/node-fetch" "^2.6.14" + +"@supabase/gotrue-js@^2.56.0": + version "2.57.0" + resolved "https://registry.yarnpkg.com/@supabase/gotrue-js/-/gotrue-js-2.57.0.tgz#3ad7f290ef934f4504aa3f6a3c2826281701d859" + integrity sha512-/CcAW40aPKgp9/w9WgXVUQFg1AOdvFR687ONOMjASPBuC6FsNbKlcXp4pc+rwKNtxyxDkBbR+x7zj/8g00r/Og== + dependencies: + "@supabase/node-fetch" "^2.6.14" + +"@supabase/node-fetch@^2.6.14": + version "2.6.15" + resolved "https://registry.yarnpkg.com/@supabase/node-fetch/-/node-fetch-2.6.15.tgz#731271430e276983191930816303c44159e7226c" + integrity sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ== + dependencies: + whatwg-url "^5.0.0" + +"@supabase/postgrest-js@^1.8.6": + version "1.9.0" + resolved "https://registry.yarnpkg.com/@supabase/postgrest-js/-/postgrest-js-1.9.0.tgz#00dddbe8119f1ec2179057e563bb54f28e6e31e3" + integrity sha512-axP6cU69jDrLbfihJKQ6vU27tklD0gzb9idkMN363MtTXeJVt5DQNT3JnJ58JVNBdL74hgm26rAsFNvHk+tnSw== + dependencies: + "@supabase/node-fetch" "^2.6.14" + +"@supabase/realtime-js@^2.8.4": + version "2.8.4" + resolved "https://registry.yarnpkg.com/@supabase/realtime-js/-/realtime-js-2.8.4.tgz#71de0874f38295ad1e780e0e6f3fbc6ae343e862" + integrity sha512-5C9slLTGikHnYmAnIBOaPogAgbcNY68vnIyE6GpqIKjHElVb6LIi4clwNcjHSj4z6szuvvzj8T/+ePEgGEGekw== + dependencies: + "@supabase/node-fetch" "^2.6.14" + "@types/phoenix" "^1.5.4" + "@types/websocket" "^1.0.3" + websocket "^1.0.34" + +"@supabase/storage-js@^2.5.4": + version "2.5.4" + resolved "https://registry.yarnpkg.com/@supabase/storage-js/-/storage-js-2.5.4.tgz#15946fa03574e94cdeff2b7fa2cd5b85880239f5" + integrity sha512-yspHD19I9uQUgfTh0J94+/r/g6hnhdQmw6Y7OWqr/EbnL6uvicGV1i1UDkkmeUHqfF9Mbt2sLtuxRycYyKv2ew== + dependencies: + "@supabase/node-fetch" "^2.6.14" + +"@supabase/supabase-js@^2.38.5": + version "2.38.5" + resolved "https://registry.yarnpkg.com/@supabase/supabase-js/-/supabase-js-2.38.5.tgz#4341c09288b9ba0ebb34862519d4ec4fb028e18e" + integrity sha512-QTXld3AfwAJgeOGyOKsCcT7AjC3jJxN02iHy299Fw+qKX0lJ1tVVhMGlga101C1stUCvgzjcypmMSGiZ2oeKsw== + dependencies: + "@supabase/functions-js" "^2.1.5" + "@supabase/gotrue-js" "^2.56.0" + "@supabase/node-fetch" "^2.6.14" + "@supabase/postgrest-js" "^1.8.6" + "@supabase/realtime-js" "^2.8.4" + "@supabase/storage-js" "^2.5.4" + +"@types/node@*", "@types/node@^20.10.0": + version "20.10.0" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.0.tgz#16ddf9c0a72b832ec4fcce35b8249cf149214617" + integrity sha512-D0WfRmU9TQ8I9PFx9Yc+EBHw+vSpIub4IDvQivcp26PtPrdMGAq5SDcpXEo/epqa/DXotVpekHiLNTg3iaKXBQ== + dependencies: + undici-types "~5.26.4" + +"@types/node@>=8.1.0": + version "20.10.2" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.10.2.tgz#32a5e8228357f57714ad28d52229ab04880c2814" + integrity sha512-37MXfxkb0vuIlRKHNxwCkb60PNBpR94u4efQuN4JgIAm66zfCDXGSAFCef9XUWFovX2R1ok6Z7MHhtdVXXkkIw== + dependencies: + undici-types "~5.26.4" + +"@types/phoenix@^1.5.4": + version "1.6.4" + resolved "https://registry.yarnpkg.com/@types/phoenix/-/phoenix-1.6.4.tgz#cceac93a827555473ad38057d1df7d06eef1ed71" + integrity sha512-B34A7uot1Cv0XtaHRYDATltAdKx0BvVKNgYNqE4WjtPUa4VQJM7kxeXcVKaH+KS+kCmZ+6w+QaUdcljiheiBJA== + +"@types/websocket@^1.0.3": + version "1.0.10" + resolved "https://registry.yarnpkg.com/@types/websocket/-/websocket-1.0.10.tgz#804b1a02780da522f5742bc184a6d16a2eb78c7c" + integrity sha512-svjGZvPB7EzuYS94cI7a+qhwgGU1y89wUgjT6E2wVUfmAGIvRfT7obBvRtnhXCSsoMdlG4gBFGE7MfkIXZLoww== + dependencies: + "@types/node" "*" + +bufferutil@^4.0.1: + version "4.0.8" + resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.8.tgz#1de6a71092d65d7766c4d8a522b261a6e787e8ea" + integrity sha512-4T53u4PdgsXqKaIctwF8ifXlRTTmEPJ8iEPWFdGZvcf7sbwYo6FKFEX9eNNAnzFZ7EzJAQ3CJeOtCRA4rDp7Pw== + dependencies: + node-gyp-build "^4.3.0" + +call-bind@^1.0.0: + version "1.0.5" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513" + integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ== + dependencies: + function-bind "^1.1.2" + get-intrinsic "^1.2.1" + set-function-length "^1.1.1" + +d@1, d@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" + integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== + dependencies: + es5-ext "^0.10.50" + type "^1.0.1" + +debug@^2.2.0: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +define-data-property@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3" + integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ== + dependencies: + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +es5-ext@^0.10.35, es5-ext@^0.10.50: + version "0.10.62" + resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5" + integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA== + dependencies: + es6-iterator "^2.0.3" + es6-symbol "^3.1.3" + next-tick "^1.1.0" + +es6-iterator@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" + integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== + dependencies: + d "1" + es5-ext "^0.10.35" + es6-symbol "^3.1.1" + +es6-symbol@^3.1.1, es6-symbol@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" + integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== + dependencies: + d "^1.0.1" + ext "^1.1.2" + +ext@^1.1.2: + version "1.7.0" + resolved "https://registry.yarnpkg.com/ext/-/ext-1.7.0.tgz#0ea4383c0103d60e70be99e9a7f11027a33c4f5f" + integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw== + dependencies: + type "^2.7.2" + +fsevents@2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +get-intrinsic@^1.0.2, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2: + version "1.2.2" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b" + integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA== + dependencies: + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +has-property-descriptors@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" + integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== + dependencies: + get-intrinsic "^1.2.2" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + dependencies: + function-bind "^1.1.2" + +is-typedarray@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +next-tick@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" + integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== + +node-gyp-build@^4.3.0: + version "4.7.1" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.7.1.tgz#cd7d2eb48e594874053150a9418ac85af83ca8f7" + integrity sha512-wTSrZ+8lsRRa3I3H8Xr65dLWSgCvY2l4AOnaeKdPA9TB/WYMPaTcrzf3rXvFoVvjKNVnu0CcWSx54qq9GKRUYg== + +object-inspect@^1.9.0: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + +playwright-core@1.40.0: + version "1.40.0" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.40.0.tgz#82f61e5504cb3097803b6f8bbd98190dd34bdf14" + integrity sha512-fvKewVJpGeca8t0ipM56jkVSU6Eo0RmFvQ/MaCQNDYm+sdvKkMBBWTE1FdeMqIdumRaXXjZChWHvIzCGM/tA/Q== + +playwright@1.40.0: + version "1.40.0" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.40.0.tgz#2a1824b9fe5c4fe52ed53db9ea68003543a99df0" + integrity sha512-gyHAgQjiDf1m34Xpwzaqb76KgfzYrhK7iih+2IzcOCoZWr/8ZqmdBw+t0RU85ZmfJMgtgAiNtBQ/KS2325INXw== + dependencies: + playwright-core "1.40.0" + optionalDependencies: + fsevents "2.3.2" + +qs@^6.11.0: + version "6.11.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9" + integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA== + dependencies: + side-channel "^1.0.4" + +set-function-length@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed" + integrity sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ== + dependencies: + define-data-property "^1.1.1" + get-intrinsic "^1.2.1" + gopd "^1.0.1" + has-property-descriptors "^1.0.0" + +side-channel@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" + integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== + dependencies: + call-bind "^1.0.0" + get-intrinsic "^1.0.2" + object-inspect "^1.9.0" + +stripe@^14.7.0: + version "14.7.0" + resolved "https://registry.yarnpkg.com/stripe/-/stripe-14.7.0.tgz#11b24a1b5651a22db829aba8b2b8b495dbd3308f" + integrity sha512-A6XWH76K0K7IgEBz4odIVxjhkdPduSPafZju6ltAFU0oMbWJlfeaDyMTfRJ9vcYA9zqENfwRizExD/2nZ4rQ1A== + dependencies: + "@types/node" ">=8.1.0" + qs "^6.11.0" + +tr46@~0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" + integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== + +type@^1.0.1: + version "1.2.0" + resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" + integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== + +type@^2.7.2: + version "2.7.2" + resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0" + integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== + +typedarray-to-buffer@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" + integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== + dependencies: + is-typedarray "^1.0.0" + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +utf-8-validate@^5.0.2: + version "5.0.10" + resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.10.tgz#d7d10ea39318171ca982718b6b96a8d2442571a2" + integrity sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ== + dependencies: + node-gyp-build "^4.3.0" + +webidl-conversions@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" + integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== + +websocket@^1.0.34: + version "1.0.34" + resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111" + integrity sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ== + dependencies: + bufferutil "^4.0.1" + debug "^2.2.0" + es5-ext "^0.10.50" + typedarray-to-buffer "^3.1.5" + utf-8-validate "^5.0.2" + yaeti "^0.0.6" + +whatwg-url@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" + integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== + dependencies: + tr46 "~0.0.3" + webidl-conversions "^3.0.0" + +yaeti@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" + integrity sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug== diff --git a/tailwind.config.js b/tailwind.config.js deleted file mode 100644 index 07c2232..0000000 --- a/tailwind.config.js +++ /dev/null @@ -1,73 +0,0 @@ -/** @type {import('tailwindcss').Config} */ -const plugin = require("tailwindcss/plugin"); - -module.exports = { - content: [ - "./src/pages/**/*.{js,ts,jsx,tsx}", - "./src/components/**/*.{js,ts,jsx,tsx}", - "node_modules/daisyui/dist/**/*.js", - "node_modules/react-daisyui/dist/**/*.js", - ], - theme: { - extend: {}, - }, - plugins: [ - require("@tailwindcss/typography"), - require("daisyui"), - plugin(({ addComponents, theme }) => { - addComponents({ - ".border-base-outline": { - borderColor: "hsl(var(--bc) / var(--tw-border-opacity))", - "--tw-border-opacity": "0.2", - }, - ".divide-base-outline": { - "&>:not([hidden])~:not([hidden])": { - borderColor: "hsl(var(--bc) / var(--tw-border-opacity))", - "--tw-border-opacity": "0.2", - }, - }, - }); - }), - plugin(({ addComponents, theme }) => { - const headings = { - ".h1": { - fontSize: theme("fontSize.2xl"), - fontWeight: theme("fontWeight.medium"), - }, - ".h2": { - fontSize: theme("fontSize.xl"), - fontWeight: theme("fontWeight.medium"), - }, - ".h3": { - fontSize: theme("fontSize.lg"), - fontWeight: theme("fontWeight.medium"), - }, - ".h4": { - fontSize: theme("fontSize.lg"), - fontWeight: theme("fontWeight.medium"), - }, - "@screen md": { - ".h1": { - fontSize: theme("fontSize.3xl"), - fontWeight: theme("fontWeight.medium"), - }, - ".h2": { - fontSize: theme("fontSize.2xl"), - fontWeight: theme("fontWeight.medium"), - }, - ".h3": { - fontSize: theme("fontSize.xl"), - fontWeight: theme("fontWeight.medium"), - }, - ".h4": { - fontSize: theme("fontSize.lg"), - fontWeight: theme("fontWeight.medium"), - }, - }, - }; - addComponents(headings, { - variants: ["responsive"], - }); - }), - ], -}; diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index df4e80c..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,39 +0,0 @@ -{ - "compilerOptions": { - "target": "es5", - "lib": [ - "dom", - "dom.iterable", - "esnext" - ], - "allowJs": true, - "skipLibCheck": true, - "strict": false, - "forceConsistentCasingInFileNames": true, - "noEmit": true, - "esModuleInterop": true, - "module": "esnext", - "moduleResolution": "node", - "resolveJsonModule": true, - "isolatedModules": true, - "jsx": "preserve", - "baseUrl": ".", - "paths": { - "@/*": [ - "src/*" - ], - "@tests/*": [ - "__tests__/setup/*" - ] - }, - "incremental": true - }, - "include": [ - "next-env.d.ts", - "**/*.ts", - "**/*.tsx" - ], - "exclude": [ - "node_modules" - ] -} diff --git a/yarn.lock b/yarn.lock deleted file mode 100644 index 4d5365e..0000000 --- a/yarn.lock +++ /dev/null @@ -1,6230 +0,0 @@ -# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. -# yarn lockfile v1 - - -"@adobe/css-tools@^4.0.1": - version "4.0.1" - resolved "https://registry.yarnpkg.com/@adobe/css-tools/-/css-tools-4.0.1.tgz#b38b444ad3aa5fedbb15f2f746dcd934226a12dd" - integrity sha512-+u76oB43nOHrF4DDWRLWDCtci7f3QJoEBigemIdIeTi1ODqjx6Tad9NCVnPRwewWlKkVab5PlK8DCtPTyX7S8g== - -"@ampproject/remapping@^2.1.0": - version "2.2.0" - resolved "https://registry.yarnpkg.com/@ampproject/remapping/-/remapping-2.2.0.tgz#56c133824780de3174aed5ab6834f3026790154d" - integrity sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w== - dependencies: - "@jridgewell/gen-mapping" "^0.1.0" - "@jridgewell/trace-mapping" "^0.3.9" - -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.18.6.tgz#3b25d38c89600baa2dcc219edfa88a74eb2c427a" - integrity sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q== - dependencies: - "@babel/highlight" "^7.18.6" - -"@babel/compat-data@^7.18.8": - version "7.18.8" - resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.18.8.tgz#2483f565faca607b8535590e84e7de323f27764d" - integrity sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ== - -"@babel/core@^7.11.6", "@babel/core@^7.12.3": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.18.10.tgz#39ad504991d77f1f3da91be0b8b949a5bc466fb8" - integrity sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw== - dependencies: - "@ampproject/remapping" "^2.1.0" - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.18.10" - "@babel/helper-compilation-targets" "^7.18.9" - "@babel/helper-module-transforms" "^7.18.9" - "@babel/helpers" "^7.18.9" - "@babel/parser" "^7.18.10" - "@babel/template" "^7.18.10" - "@babel/traverse" "^7.18.10" - "@babel/types" "^7.18.10" - convert-source-map "^1.7.0" - debug "^4.1.0" - gensync "^1.0.0-beta.2" - json5 "^2.2.1" - semver "^6.3.0" - -"@babel/generator@^7.18.10", "@babel/generator@^7.7.2": - version "7.18.12" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.18.12.tgz#fa58daa303757bd6f5e4bbca91b342040463d9f4" - integrity sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg== - dependencies: - "@babel/types" "^7.18.10" - "@jridgewell/gen-mapping" "^0.3.2" - jsesc "^2.5.1" - -"@babel/helper-compilation-targets@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz#69e64f57b524cde3e5ff6cc5a9f4a387ee5563bf" - integrity sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg== - dependencies: - "@babel/compat-data" "^7.18.8" - "@babel/helper-validator-option" "^7.18.6" - browserslist "^4.20.2" - semver "^6.3.0" - -"@babel/helper-environment-visitor@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz#0c0cee9b35d2ca190478756865bb3528422f51be" - integrity sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg== - -"@babel/helper-function-name@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.18.9.tgz#940e6084a55dee867d33b4e487da2676365e86b0" - integrity sha512-fJgWlZt7nxGksJS9a0XdSaI4XvpExnNIgRP+rVefWh5U7BL8pPuir6SJUmFKRfjWQ51OtWSzwOxhaH/EBWWc0A== - dependencies: - "@babel/template" "^7.18.6" - "@babel/types" "^7.18.9" - -"@babel/helper-hoist-variables@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz#d4d2c8fb4baeaa5c68b99cc8245c56554f926678" - integrity sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-module-imports@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz#1e3ebdbbd08aad1437b428c50204db13c5a3ca6e" - integrity sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-module-transforms@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz#5a1079c005135ed627442df31a42887e80fcb712" - integrity sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g== - dependencies: - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-module-imports" "^7.18.6" - "@babel/helper-simple-access" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/helper-validator-identifier" "^7.18.6" - "@babel/template" "^7.18.6" - "@babel/traverse" "^7.18.9" - "@babel/types" "^7.18.9" - -"@babel/helper-plugin-utils@^7.0.0", "@babel/helper-plugin-utils@^7.10.4", "@babel/helper-plugin-utils@^7.12.13", "@babel/helper-plugin-utils@^7.14.5", "@babel/helper-plugin-utils@^7.18.6", "@babel/helper-plugin-utils@^7.8.0": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.18.9.tgz#4b8aea3b069d8cb8a72cdfe28ddf5ceca695ef2f" - integrity sha512-aBXPT3bmtLryXaoJLyYPXPlSD4p1ld9aYeR+sJNOZjJJGiOpb+fKfh3NkcCu7J54nUJwCERPBExCCpyCOHnu/w== - -"@babel/helper-simple-access@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz#d6d8f51f4ac2978068df934b569f08f29788c7ea" - integrity sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-split-export-declaration@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz#7367949bc75b20c6d5a5d4a97bba2824ae8ef075" - integrity sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA== - dependencies: - "@babel/types" "^7.18.6" - -"@babel/helper-string-parser@^7.18.10": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz#181f22d28ebe1b3857fa575f5c290b1aaf659b56" - integrity sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw== - -"@babel/helper-validator-identifier@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.18.6.tgz#9c97e30d31b2b8c72a1d08984f2ca9b574d7a076" - integrity sha512-MmetCkz9ej86nJQV+sFCxoGGrUbU3q02kgLciwkrt9QqEB7cP39oKEY0PakknEO0Gu20SskMRi+AYZ3b1TpN9g== - -"@babel/helper-validator-option@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz#bf0d2b5a509b1f336099e4ff36e1a63aa5db4db8" - integrity sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw== - -"@babel/helpers@^7.18.9": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.18.9.tgz#4bef3b893f253a1eced04516824ede94dcfe7ff9" - integrity sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ== - dependencies: - "@babel/template" "^7.18.6" - "@babel/traverse" "^7.18.9" - "@babel/types" "^7.18.9" - -"@babel/highlight@^7.18.6": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.18.6.tgz#81158601e93e2563795adcbfbdf5d64be3f2ecdf" - integrity sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g== - dependencies: - "@babel/helper-validator-identifier" "^7.18.6" - chalk "^2.0.0" - js-tokens "^4.0.0" - -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.18.10", "@babel/parser@^7.18.11": - version "7.18.11" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.18.11.tgz#68bb07ab3d380affa9a3f96728df07969645d2d9" - integrity sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ== - -"@babel/plugin-syntax-async-generators@^7.8.4": - version "7.8.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" - integrity sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-bigint@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz#4c9a6f669f5d0cdf1b90a1671e9a146be5300cea" - integrity sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-class-properties@^7.8.3": - version "7.12.13" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz#b5c987274c4a3a82b89714796931a6b53544ae10" - integrity sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA== - dependencies: - "@babel/helper-plugin-utils" "^7.12.13" - -"@babel/plugin-syntax-import-meta@^7.8.3": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz#ee601348c370fa334d2207be158777496521fd51" - integrity sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-json-strings@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz#01ca21b668cd8218c9e640cb6dd88c5412b2c96a" - integrity sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-jsx@^7.7.2": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.18.6.tgz#a8feef63b010150abd97f1649ec296e849943ca0" - integrity sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/plugin-syntax-logical-assignment-operators@^7.8.3": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz#ca91ef46303530448b906652bac2e9fe9941f699" - integrity sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-nullish-coalescing-operator@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz#167ed70368886081f74b5c36c65a88c03b66d1a9" - integrity sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-numeric-separator@^7.8.3": - version "7.10.4" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz#b9b070b3e33570cd9fd07ba7fa91c0dd37b9af97" - integrity sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug== - dependencies: - "@babel/helper-plugin-utils" "^7.10.4" - -"@babel/plugin-syntax-object-rest-spread@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz#60e225edcbd98a640332a2e72dd3e66f1af55871" - integrity sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-catch-binding@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz#6111a265bcfb020eb9efd0fdfd7d26402b9ed6c1" - integrity sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-optional-chaining@^7.8.3": - version "7.8.3" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz#4f69c2ab95167e0180cd5336613f8c5788f7d48a" - integrity sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg== - dependencies: - "@babel/helper-plugin-utils" "^7.8.0" - -"@babel/plugin-syntax-top-level-await@^7.8.3": - version "7.14.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz#c1cfdadc35a646240001f06138247b741c34d94c" - integrity sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw== - dependencies: - "@babel/helper-plugin-utils" "^7.14.5" - -"@babel/plugin-syntax-typescript@^7.7.2": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.18.6.tgz#1c09cd25795c7c2b8a4ba9ae49394576d4133285" - integrity sha512-mAWAuq4rvOepWCBid55JuRNvpTNf2UGVgoz4JV0fXEKolsVZDzsa4NqCef758WZJj/GDu0gVGItjKFiClTAmZA== - dependencies: - "@babel/helper-plugin-utils" "^7.18.6" - -"@babel/runtime-corejs3@^7.10.2": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.18.9.tgz#7bacecd1cb2dd694eacd32a91fcf7021c20770ae" - integrity sha512-qZEWeccZCrHA2Au4/X05QW5CMdm4VjUDCrGq5gf1ZDcM4hRqreKrtwAn7yci9zfgAS9apvnsFXiGBHBAxZdK9A== - dependencies: - core-js-pure "^3.20.2" - regenerator-runtime "^0.13.4" - -"@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.18.9", "@babel/runtime@^7.9.2": - version "7.18.9" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.9.tgz#b4fcfce55db3d2e5e080d2490f608a3b9f407f4a" - integrity sha512-lkqXDcvlFT5rvEjiu6+QYO+1GXrEHRo2LOtS7E4GtX5ESIZOgepqsZBVIj6Pv+a6zqsya9VCgiK1KAK4BvJDAw== - dependencies: - regenerator-runtime "^0.13.4" - -"@babel/template@^7.18.10", "@babel/template@^7.18.6", "@babel/template@^7.3.3": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.18.10.tgz#6f9134835970d1dbf0835c0d100c9f38de0c5e71" - integrity sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/parser" "^7.18.10" - "@babel/types" "^7.18.10" - -"@babel/traverse@^7.18.10", "@babel/traverse@^7.18.9", "@babel/traverse@^7.7.2": - version "7.18.11" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.18.11.tgz#3d51f2afbd83ecf9912bcbb5c4d94e3d2ddaa16f" - integrity sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ== - dependencies: - "@babel/code-frame" "^7.18.6" - "@babel/generator" "^7.18.10" - "@babel/helper-environment-visitor" "^7.18.9" - "@babel/helper-function-name" "^7.18.9" - "@babel/helper-hoist-variables" "^7.18.6" - "@babel/helper-split-export-declaration" "^7.18.6" - "@babel/parser" "^7.18.11" - "@babel/types" "^7.18.10" - debug "^4.1.0" - globals "^11.1.0" - -"@babel/types@^7.0.0", "@babel/types@^7.18.10", "@babel/types@^7.18.6", "@babel/types@^7.18.9", "@babel/types@^7.3.0", "@babel/types@^7.3.3": - version "7.18.10" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.18.10.tgz#4908e81b6b339ca7c6b7a555a5fc29446f26dde6" - integrity sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ== - dependencies: - "@babel/helper-string-parser" "^7.18.10" - "@babel/helper-validator-identifier" "^7.18.6" - to-fast-properties "^2.0.0" - -"@bcoe/v8-coverage@^0.2.3": - version "0.2.3" - resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" - integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== - -"@eslint/eslintrc@^1.3.3": - version "1.3.3" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.3.3.tgz#2b044ab39fdfa75b4688184f9e573ce3c5b0ff95" - integrity sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg== - dependencies: - ajv "^6.12.4" - debug "^4.3.2" - espree "^9.4.0" - globals "^13.15.0" - ignore "^5.2.0" - import-fresh "^3.2.1" - js-yaml "^4.1.0" - minimatch "^3.1.2" - strip-json-comments "^3.1.1" - -"@heroicons/react@^1.0.6": - version "1.0.6" - resolved "https://registry.yarnpkg.com/@heroicons/react/-/react-1.0.6.tgz#35dd26987228b39ef2316db3b1245c42eb19e324" - integrity sha512-JJCXydOFWMDpCP4q13iEplA503MQO3xLoZiKum+955ZCtHINWnx26CUxVxxFQu/uLb4LW3ge15ZpzIkXKkJ8oQ== - -"@humanwhocodes/config-array@^0.11.6": - version "0.11.6" - resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.11.6.tgz#6a51d603a3aaf8d4cf45b42b3f2ac9318a4adc4b" - integrity sha512-jJr+hPTJYKyDILJfhNSHsjiwXYf26Flsz8DvNndOsHs5pwSnpGUEy8yzF0JYhCEvTDdV2vuOK5tt8BVhwO5/hg== - dependencies: - "@humanwhocodes/object-schema" "^1.2.1" - debug "^4.1.1" - minimatch "^3.0.4" - -"@humanwhocodes/module-importer@^1.0.1": - version "1.0.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" - integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== - -"@humanwhocodes/object-schema@^1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45" - integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA== - -"@istanbuljs/load-nyc-config@^1.0.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz#fd3db1d59ecf7cf121e80650bb86712f9b55eced" - integrity sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ== - dependencies: - camelcase "^5.3.1" - find-up "^4.1.0" - get-package-type "^0.1.0" - js-yaml "^3.13.1" - resolve-from "^5.0.0" - -"@istanbuljs/schema@^0.1.2": - version "0.1.3" - resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" - integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== - -"@jest/console@^29.2.1": - version "29.2.1" - resolved "https://registry.yarnpkg.com/@jest/console/-/console-29.2.1.tgz#5f2c62dcdd5ce66e94b6d6729e021758bceea090" - integrity sha512-MF8Adcw+WPLZGBiNxn76DOuczG3BhODTcMlDCA4+cFi41OkaY/lyI0XUUhi73F88Y+7IHoGmD80pN5CtxQUdSw== - dependencies: - "@jest/types" "^29.2.1" - "@types/node" "*" - chalk "^4.0.0" - jest-message-util "^29.2.1" - jest-util "^29.2.1" - slash "^3.0.0" - -"@jest/core@^29.3.0": - version "29.3.0" - resolved "https://registry.yarnpkg.com/@jest/core/-/core-29.3.0.tgz#7042d3fd673b51d89d6f6bf8b9f85fb65573629e" - integrity sha512-5DyNvV8452bwqcYyXHCYaAD8UrTiWosrhBY+rc0MBMyXyDzcIL+w5gdlCYhlHbNsHoWnf4nUbRmg++LWfWVtMQ== - dependencies: - "@jest/console" "^29.2.1" - "@jest/reporters" "^29.3.0" - "@jest/test-result" "^29.2.1" - "@jest/transform" "^29.3.0" - "@jest/types" "^29.2.1" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - ci-info "^3.2.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - jest-changed-files "^29.2.0" - jest-config "^29.3.0" - jest-haste-map "^29.3.0" - jest-message-util "^29.2.1" - jest-regex-util "^29.2.0" - jest-resolve "^29.3.0" - jest-resolve-dependencies "^29.3.0" - jest-runner "^29.3.0" - jest-runtime "^29.3.0" - jest-snapshot "^29.3.0" - jest-util "^29.2.1" - jest-validate "^29.2.2" - jest-watcher "^29.2.2" - micromatch "^4.0.4" - pretty-format "^29.2.1" - slash "^3.0.0" - strip-ansi "^6.0.0" - -"@jest/environment@^29.3.0": - version "29.3.0" - resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-29.3.0.tgz#e7bfe22531813f86040feb75c046faab32fd534d" - integrity sha512-8wgn3br51bx+7rgC8FOKmAD62Q39iswdiy5/p6acoekp/9Bb/IQbh3zydOrnGp74LwStSrKgpQSKBlOKlAQq0g== - dependencies: - "@jest/fake-timers" "^29.3.0" - "@jest/types" "^29.2.1" - "@types/node" "*" - jest-mock "^29.3.0" - -"@jest/expect-utils@^29.0.3": - version "29.0.3" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.0.3.tgz#f5bb86f5565bf2dacfca31ccbd887684936045b2" - integrity sha512-i1xUkau7K/63MpdwiRqaxgZOjxYs4f0WMTGJnYwUKubsNRZSeQbLorS7+I4uXVF9KQ5r61BUPAUMZ7Lf66l64Q== - dependencies: - jest-get-type "^29.0.0" - -"@jest/expect-utils@^29.2.2": - version "29.2.2" - resolved "https://registry.yarnpkg.com/@jest/expect-utils/-/expect-utils-29.2.2.tgz#460a5b5a3caf84d4feb2668677393dd66ff98665" - integrity sha512-vwnVmrVhTmGgQzyvcpze08br91OL61t9O0lJMDyb6Y/D8EKQ9V7rGUb/p7PDt0GPzK0zFYqXWFo4EO2legXmkg== - dependencies: - jest-get-type "^29.2.0" - -"@jest/expect@^29.3.0": - version "29.3.0" - resolved "https://registry.yarnpkg.com/@jest/expect/-/expect-29.3.0.tgz#06907ffc02541c8d5186e8324765a72f391f3125" - integrity sha512-Lz/3x4Se5g6nBuLjTO+xE8D4OXY9fFmosZPwkXXZUJUsp9r9seN81cJa54wOGr1QjCQnhngMqclblhM4X/hcCg== - dependencies: - expect "^29.3.0" - jest-snapshot "^29.3.0" - -"@jest/fake-timers@^29.3.0": - version "29.3.0" - resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-29.3.0.tgz#ffa74e5b2937676849866cac79cac6a742697f00" - integrity sha512-SzmWtN6Rld+xebMRGuWeMGhytc7qHnYfFk1Zd/1QavQWsFOmA9SgtvGHCBue1wXQhdDMaSIm1aPGj2Zmyrr1Zg== - dependencies: - "@jest/types" "^29.2.1" - "@sinonjs/fake-timers" "^9.1.2" - "@types/node" "*" - jest-message-util "^29.2.1" - jest-mock "^29.3.0" - jest-util "^29.2.1" - -"@jest/globals@^29.3.0": - version "29.3.0" - resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-29.3.0.tgz#f58c14d727fd7d02d7851bc03fc0445eefa2dbe2" - integrity sha512-okYDVzYNrt/4ysR8XnX6u0I1bGG4kmfdXtUu7kwWHZ9OP13RCjmphgve0tfOrNluwksWvOPYS1f/HOrFTHLygQ== - dependencies: - "@jest/environment" "^29.3.0" - "@jest/expect" "^29.3.0" - "@jest/types" "^29.2.1" - jest-mock "^29.3.0" - -"@jest/reporters@^29.3.0": - version "29.3.0" - resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-29.3.0.tgz#e5e2af97d7754510393d7c04084744841cce2eaf" - integrity sha512-MV76tB3Kd80vcv2yMDZfQpMkwkHaY9hlvVhCtHXkVRCWwN+SX3EOmCdX8pT/X4Xh+NusA7l2Rc3yhx4q5p3+Fg== - dependencies: - "@bcoe/v8-coverage" "^0.2.3" - "@jest/console" "^29.2.1" - "@jest/test-result" "^29.2.1" - "@jest/transform" "^29.3.0" - "@jest/types" "^29.2.1" - "@jridgewell/trace-mapping" "^0.3.15" - "@types/node" "*" - chalk "^4.0.0" - collect-v8-coverage "^1.0.0" - exit "^0.1.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - istanbul-lib-coverage "^3.0.0" - istanbul-lib-instrument "^5.1.0" - istanbul-lib-report "^3.0.0" - istanbul-lib-source-maps "^4.0.0" - istanbul-reports "^3.1.3" - jest-message-util "^29.2.1" - jest-util "^29.2.1" - jest-worker "^29.3.0" - slash "^3.0.0" - string-length "^4.0.1" - strip-ansi "^6.0.0" - v8-to-istanbul "^9.0.1" - -"@jest/schemas@^28.1.3": - version "28.1.3" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-28.1.3.tgz#ad8b86a66f11f33619e3d7e1dcddd7f2d40ff905" - integrity sha512-/l/VWsdt/aBXgjshLWOFyFt3IVdYypu5y2Wn2rOO1un6nkqIn8SLXzgIMYXFyYsRWDyF5EthmKJMIdJvk08grg== - dependencies: - "@sinclair/typebox" "^0.24.1" - -"@jest/schemas@^29.0.0": - version "29.0.0" - resolved "https://registry.yarnpkg.com/@jest/schemas/-/schemas-29.0.0.tgz#5f47f5994dd4ef067fb7b4188ceac45f77fe952a" - integrity sha512-3Ab5HgYIIAnS0HjqJHQYZS+zXc4tUmTmBH3z83ajI6afXp8X3ZtdLX+nXx+I7LNkJD7uN9LAVhgnjDgZa2z0kA== - dependencies: - "@sinclair/typebox" "^0.24.1" - -"@jest/source-map@^29.2.0": - version "29.2.0" - resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-29.2.0.tgz#ab3420c46d42508dcc3dc1c6deee0b613c235744" - integrity sha512-1NX9/7zzI0nqa6+kgpSdKPK+WU1p+SJk3TloWZf5MzPbxri9UEeXX5bWZAPCzbQcyuAzubcdUHA7hcNznmRqWQ== - dependencies: - "@jridgewell/trace-mapping" "^0.3.15" - callsites "^3.0.0" - graceful-fs "^4.2.9" - -"@jest/test-result@^29.2.1": - version "29.2.1" - resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-29.2.1.tgz#f42dbf7b9ae465d0a93eee6131473b8bb3bd2edb" - integrity sha512-lS4+H+VkhbX6z64tZP7PAUwPqhwj3kbuEHcaLuaBuB+riyaX7oa1txe0tXgrFj5hRWvZKvqO7LZDlNWeJ7VTPA== - dependencies: - "@jest/console" "^29.2.1" - "@jest/types" "^29.2.1" - "@types/istanbul-lib-coverage" "^2.0.0" - collect-v8-coverage "^1.0.0" - -"@jest/test-sequencer@^29.3.0": - version "29.3.0" - resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-29.3.0.tgz#0c2198fe482c26d763abbcb183992ae769bb7978" - integrity sha512-XQlTP/S6Yf6NKV0Mt4oopFKyDxiEkDMD7hIFcCTeltKQszE0Z+LI5KLukwNW6Qxr1YzaZ/s6PlKJusiCLJNTcw== - dependencies: - "@jest/test-result" "^29.2.1" - graceful-fs "^4.2.9" - jest-haste-map "^29.3.0" - slash "^3.0.0" - -"@jest/transform@^29.3.0": - version "29.3.0" - resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-29.3.0.tgz#7f71c9596d5bad1613a3a5eb26729dd84fc71a5a" - integrity sha512-4T8h61ItCakAlJkdYa7XVWP3r39QldlCeOSNmRpiJisi5PrrlzwZdpJDIH13ZZjh+MlSPQ2cq8YbUs3TuH+tRA== - dependencies: - "@babel/core" "^7.11.6" - "@jest/types" "^29.2.1" - "@jridgewell/trace-mapping" "^0.3.15" - babel-plugin-istanbul "^6.1.1" - chalk "^4.0.0" - convert-source-map "^2.0.0" - fast-json-stable-stringify "^2.1.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.3.0" - jest-regex-util "^29.2.0" - jest-util "^29.2.1" - micromatch "^4.0.4" - pirates "^4.0.4" - slash "^3.0.0" - write-file-atomic "^4.0.1" - -"@jest/types@^29.0.3": - version "29.0.3" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.0.3.tgz#0be78fdddb1a35aeb2041074e55b860561c8ef63" - integrity sha512-coBJmOQvurXjN1Hh5PzF7cmsod0zLIOXpP8KD161mqNlroMhLcwpODiEzi7ZsRl5Z/AIuxpeNm8DCl43F4kz8A== - dependencies: - "@jest/schemas" "^29.0.0" - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^17.0.8" - chalk "^4.0.0" - -"@jest/types@^29.2.1": - version "29.2.1" - resolved "https://registry.yarnpkg.com/@jest/types/-/types-29.2.1.tgz#ec9c683094d4eb754e41e2119d8bdaef01cf6da0" - integrity sha512-O/QNDQODLnINEPAI0cl9U6zUIDXEWXt6IC1o2N2QENuos7hlGUIthlKyV4p6ki3TvXFX071blj8HUhgLGquPjw== - dependencies: - "@jest/schemas" "^29.0.0" - "@types/istanbul-lib-coverage" "^2.0.0" - "@types/istanbul-reports" "^3.0.0" - "@types/node" "*" - "@types/yargs" "^17.0.8" - chalk "^4.0.0" - -"@jridgewell/gen-mapping@^0.1.0": - version "0.1.1" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz#e5d2e450306a9491e3bd77e323e38d7aff315996" - integrity sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w== - dependencies: - "@jridgewell/set-array" "^1.0.0" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@jridgewell/gen-mapping@^0.3.2": - version "0.3.2" - resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz#c1aedc61e853f2bb9f5dfe6d4442d3b565b253b9" - integrity sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A== - dependencies: - "@jridgewell/set-array" "^1.0.1" - "@jridgewell/sourcemap-codec" "^1.4.10" - "@jridgewell/trace-mapping" "^0.3.9" - -"@jridgewell/resolve-uri@^3.0.3": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" - integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== - -"@jridgewell/set-array@^1.0.0", "@jridgewell/set-array@^1.0.1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" - integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== - -"@jridgewell/sourcemap-codec@^1.4.10": - version "1.4.14" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== - -"@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.15", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.15" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.15.tgz#aba35c48a38d3fd84b37e66c9c0423f9744f9774" - integrity sha512-oWZNOULl+UbhsgB51uuZzglikfIKSUBO/M9W2OfEjn7cmqoAiCgmv9lyACTUacZwBz0ITnJ2NqjU8Tx0DHL88g== - dependencies: - "@jridgewell/resolve-uri" "^3.0.3" - "@jridgewell/sourcemap-codec" "^1.4.10" - -"@mdx-js/mdx@^2.0.0": - version "2.1.3" - resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-2.1.3.tgz#d5821920ebe546b45192f4c7a64dcc68a658f7f9" - integrity sha512-ahbb47HJIJ4xnifaL06tDJiSyLEy1EhFAStO7RZIm3GTa7yGW3NGhZaj+GUCveFgl5oI54pY4BgiLmYm97y+zg== - dependencies: - "@types/estree-jsx" "^1.0.0" - "@types/mdx" "^2.0.0" - estree-util-build-jsx "^2.0.0" - estree-util-is-identifier-name "^2.0.0" - estree-util-to-js "^1.1.0" - estree-walker "^3.0.0" - hast-util-to-estree "^2.0.0" - markdown-extensions "^1.0.0" - periscopic "^3.0.0" - remark-mdx "^2.0.0" - remark-parse "^10.0.0" - remark-rehype "^10.0.0" - unified "^10.0.0" - unist-util-position-from-estree "^1.0.0" - unist-util-stringify-position "^3.0.0" - unist-util-visit "^4.0.0" - vfile "^5.0.0" - -"@mdx-js/react@^2.0.0": - version "2.1.3" - resolved "https://registry.yarnpkg.com/@mdx-js/react/-/react-2.1.3.tgz#4b28a774295ed1398cf6be1b8ddef69d6a30e78d" - integrity sha512-11n4lTvvRyxq3OYbWJwEYM+7q6PE0GxKbk0AwYIIQmrRkxDeljIsjDQkKOgdr/orgRRbYy5zi+iERdnwe01CHQ== - dependencies: - "@types/mdx" "^2.0.0" - "@types/react" ">=16" - -"@next/env@13.0.2": - version "13.0.2" - resolved "https://registry.yarnpkg.com/@next/env/-/env-13.0.2.tgz#5fbd7b4146175ae406edfb4a38b62de8c880c09d" - integrity sha512-Qb6WPuRriGIQ19qd6NBxpcrFOfj8ziN7l9eZUfwff5gl4zLXluqtuZPddYZM/oWjN53ZYcuRXzL+oowKyJeYtA== - -"@next/eslint-plugin-next@13.0.2": - version "13.0.2" - resolved "https://registry.yarnpkg.com/@next/eslint-plugin-next/-/eslint-plugin-next-13.0.2.tgz#89fe2144b37896f926e2bd9bed675396f1f697ce" - integrity sha512-W+fIIIaFU7Kct7Okx91C7XDRGolv/w2RUenX2yZFeeNVcuVzDIKUcNmckrYbYcwrNQUSXmtwrs3g8xwast0YtA== - dependencies: - glob "7.1.7" - -"@next/swc-android-arm-eabi@13.0.2": - version "13.0.2" - resolved "https://registry.yarnpkg.com/@next/swc-android-arm-eabi/-/swc-android-arm-eabi-13.0.2.tgz#66669b8aab5062f554b8e9905d855679aabf0342" - integrity sha512-X54UQCTFyOGnJP//Z71dPPlp4BCYcQL2ncikKXQcPzVpqPs4C3m+tKC8ivBNH6edAXkppwsLRz1/yQwgSZ9Swg== - -"@next/swc-android-arm64@13.0.2": - version "13.0.2" - resolved "https://registry.yarnpkg.com/@next/swc-android-arm64/-/swc-android-arm64-13.0.2.tgz#c0641d30525e0fb22bf1e2baf6c461d2d9e52f1a" - integrity sha512-1P00Kv8uKaLubqo7JzPrTqgFAzSOmfb8iwqJrOb9in5IvTRtNGlkR4hU0sXzqbQNM/+SaYxze6Z5ry1IDyb/cQ== - -"@next/swc-darwin-arm64@13.0.2": - version "13.0.2" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.0.2.tgz#d7e01a33393e83456dbfdc41446bb8a923968ff7" - integrity sha512-1zGIOkInkOLRv0QQGZ+3wffYsyKI4vIy62LYTvDWUn7TAYqnmXwougp9NSLqDeagLwgsv2URrykyAFixA/YqxA== - -"@next/swc-darwin-x64@13.0.2": - version "13.0.2" - resolved "https://registry.yarnpkg.com/@next/swc-darwin-x64/-/swc-darwin-x64-13.0.2.tgz#d4a3fbe51edf871a675d89a7afdc78174d1e5841" - integrity sha512-ECDAjoMP1Y90cARaelS6X+k6BQx+MikAYJ8f/eaJrLur44NIOYc9HA/dgcTp5jenguY4yT8V+HCquLjAVle6fA== - -"@next/swc-freebsd-x64@13.0.2": - version "13.0.2" - resolved "https://registry.yarnpkg.com/@next/swc-freebsd-x64/-/swc-freebsd-x64-13.0.2.tgz#1b54c3f38d3b36f86663a8dfcc81ea05e01f5172" - integrity sha512-2DcL/ofQdBnQX3IoI9sjlIAyLCD1oZoUBuhrhWbejvBQjutWrI0JTEv9uG69WcxWhVMm3BCsjv8GK2/68OKp7A== - -"@next/swc-linux-arm-gnueabihf@13.0.2": - version "13.0.2" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm-gnueabihf/-/swc-linux-arm-gnueabihf-13.0.2.tgz#18495cff32c0b2182cfbf677614219d7072214da" - integrity sha512-Y3OQF1CSBSWW2vGkmvOIuOUNqOq8qX7f1ZpcKUVWP3/Uq++DZmVi9d18lgnSe1I3QFqc+nXWyun9ljsN83j0sw== - -"@next/swc-linux-arm64-gnu@13.0.2": - version "13.0.2" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.0.2.tgz#5fb2563651166c3c6f32bf9e2f9cc6a16a9ef783" - integrity sha512-mNyzwsFF6kwZYEjnGicx9ksDZYEZvyzEc1BtCu8vdZi/v8UeixQwCiAT6FyYX9uxMPEkzk8qiU0t0u9gvltsKw== - -"@next/swc-linux-arm64-musl@13.0.2": - version "13.0.2" - resolved "https://registry.yarnpkg.com/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.0.2.tgz#b9f33c5e17cfe04aa5769b717284cf80865761e6" - integrity sha512-M6SdYjWgRrY3tJBxz0663zCRPTu5BRONmxlftKWWHv9LjAJ59neTLaGj4rp0A08DkJglZIoCkLOzLrzST6TGag== - -"@next/swc-linux-x64-gnu@13.0.2": - version "13.0.2" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.0.2.tgz#29efcc2fd0122689d7e06c5b6b0883fe495db2bf" - integrity sha512-pi63RoxvG4ES1KS06Zpm0MATVIXTs/TIbLbdckeLoM40u1d3mQl/+hSSrLRSxzc2OtyL8fh92sM4gkJrQXAMAw== - -"@next/swc-linux-x64-musl@13.0.2": - version "13.0.2" - resolved "https://registry.yarnpkg.com/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.0.2.tgz#d68fdf6eefc57813fa91559c7089b49d6131ecab" - integrity sha512-9Pv91gfYnDONgjtRm78n64b/c54+azeHtlnqBLTnIFWSMBDRl1/WDkhKWIj3fBGPLimtK7Tko3ULR3og9RRUPw== - -"@next/swc-win32-arm64-msvc@13.0.2": - version "13.0.2" - resolved "https://registry.yarnpkg.com/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.0.2.tgz#acdcb3023045f60cca510f659a2349895e6405bd" - integrity sha512-Nvewe6YZaizAkGHHprbMkYqQulBjZCHKBGKeFPwoPtOA+a2Qi4pZzc/qXFyC5/2A6Z0mr2U1zg9rd04WBYMwBw== - -"@next/swc-win32-ia32-msvc@13.0.2": - version "13.0.2" - resolved "https://registry.yarnpkg.com/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.0.2.tgz#a78ee9211471768febac9df915c2a8dbbcd05e41" - integrity sha512-ZUBYGZw5G3QrqDpRq1EWi3aHmvPZM8ijK5TFL6UbH16cYQ0JpANmuG2P66KB93Qe/lWWzbeAZk/tj1XqwoCuPA== - -"@next/swc-win32-x64-msvc@13.0.2": - version "13.0.2" - resolved "https://registry.yarnpkg.com/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.0.2.tgz#e86c2de910cd68a17974db5556d4588737412c68" - integrity sha512-fA9uW1dm7C0mEYGcKlbmLcVm2sKcye+1kPxh2cM4jVR+kQQMtHWsjIzeSpe2grQLSDan06z4n6hbr8b1c3hA8w== - -"@nodelib/fs.scandir@2.1.5": - version "2.1.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" - integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== - dependencies: - "@nodelib/fs.stat" "2.0.5" - run-parallel "^1.1.9" - -"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" - integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== - -"@nodelib/fs.walk@^1.2.3", "@nodelib/fs.walk@^1.2.8": - version "1.2.8" - resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" - integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== - dependencies: - "@nodelib/fs.scandir" "2.1.5" - fastq "^1.6.0" - -"@resvg/resvg-wasm@2.0.0-alpha.4": - version "2.0.0-alpha.4" - resolved "https://registry.yarnpkg.com/@resvg/resvg-wasm/-/resvg-wasm-2.0.0-alpha.4.tgz#fc2f86186a9641df030d8f9f3f9d995899cd1ecb" - integrity sha512-pWIG9a/x1ky8gXKRhPH1OPKpHFoMN1ISLbJ+O+gPXQHIAKhNd5I28RlWf7q576hAOQA9JZTlo3p/M2uyLzJmmw== - -"@rushstack/eslint-patch@^1.1.3": - version "1.1.4" - resolved "https://registry.yarnpkg.com/@rushstack/eslint-patch/-/eslint-patch-1.1.4.tgz#0c8b74c50f29ee44f423f7416829c0bf8bb5eb27" - integrity sha512-LwzQKA4vzIct1zNZzBmRKI9QuNpLgTQMEjsQLf3BXuGYb3QPTP4Yjf6mkdX+X1mYttZ808QpOwAzZjv28kq7DA== - -"@shuding/opentype.js@1.4.0-beta.0": - version "1.4.0-beta.0" - resolved "https://registry.yarnpkg.com/@shuding/opentype.js/-/opentype.js-1.4.0-beta.0.tgz#5d1e7e9e056f546aad41df1c5043f8f85d39e24b" - integrity sha512-3NgmNyH3l/Hv6EvsWJbsvpcpUba6R8IREQ83nH83cyakCw7uM1arZKNfHwv1Wz6jgqrF/j4x5ELvR6PnK9nTcA== - dependencies: - fflate "^0.7.3" - string.prototype.codepointat "^0.2.1" - -"@sinclair/typebox@^0.24.1": - version "0.24.28" - resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.24.28.tgz#15aa0b416f82c268b1573ab653e4413c965fe794" - integrity sha512-dgJd3HLOkLmz4Bw50eZx/zJwtBq65nms3N9VBYu5LTjJ883oBFkTyXRlCB/ZGGwqYpJJHA5zW2Ibhl5ngITfow== - -"@sinonjs/commons@^1.7.0": - version "1.8.3" - resolved "https://registry.yarnpkg.com/@sinonjs/commons/-/commons-1.8.3.tgz#3802ddd21a50a949b6721ddd72da36e67e7f1b2d" - integrity sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ== - dependencies: - type-detect "4.0.8" - -"@sinonjs/fake-timers@^9.1.2": - version "9.1.2" - resolved "https://registry.yarnpkg.com/@sinonjs/fake-timers/-/fake-timers-9.1.2.tgz#4eaab737fab77332ab132d396a3c0d364bd0ea8c" - integrity sha512-BPS4ynJW/o92PUR4wgriz2Ud5gpST5vz6GQfMixEDK0Z8ZCUv2M7SkBLykH56T++Xs+8ln9zTGbOvNGIe02/jw== - dependencies: - "@sinonjs/commons" "^1.7.0" - -"@supabase/auth-helpers-nextjs@^0.5.1": - version "0.5.1" - resolved "https://registry.yarnpkg.com/@supabase/auth-helpers-nextjs/-/auth-helpers-nextjs-0.5.1.tgz#f9ebec1a5056276b471f03a4a1edc3cc429eef3f" - integrity sha512-aXpFlg3XUN70fX+uqsxZCTYe4u0c9yQuSYxTxvxZFAQuRPGxv86zoxUAslMFMIvQN8uwb8dUjlTsqb1e0qTEPQ== - dependencies: - "@supabase/auth-helpers-shared" "0.2.3" - -"@supabase/auth-helpers-react@^0.3.1": - version "0.3.1" - resolved "https://registry.yarnpkg.com/@supabase/auth-helpers-react/-/auth-helpers-react-0.3.1.tgz#815493a294662c56b83c7dd51692dacd24329342" - integrity sha512-g3SFv08Dz9FapNif/ZY1b7qKGlMJDyTLSayHBz3kb3FuYxg7aLWgQtydDhm5AGbc0XtvpIBuhGTIOVevwpdosA== - -"@supabase/auth-helpers-shared@0.2.3": - version "0.2.3" - resolved "https://registry.yarnpkg.com/@supabase/auth-helpers-shared/-/auth-helpers-shared-0.2.3.tgz#da2cd1144f016c61b30ee6ac0da0f3dac7ccc99e" - integrity sha512-Xwnd2UQ/VTjTKIuVg1Xl/ryrElbSccOJhC11jbVPHOs7Y6yxzy9APxQs//jj4IpbDH4uOEDCdpMIJ0tzRxj9DQ== - -"@supabase/functions-js@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@supabase/functions-js/-/functions-js-2.0.0.tgz#4ba0c9e6dff031e17666bef6779c48eff290a8a0" - integrity sha512-ozb7bds2yvf5k7NM2ZzUkxvsx4S4i2eRKFSJetdTADV91T65g4gCzEs9L3LUXSrghcGIkUaon03VPzOrFredqg== - dependencies: - cross-fetch "^3.1.5" - -"@supabase/gotrue-js@^2.2.2": - version "2.2.3" - resolved "https://registry.yarnpkg.com/@supabase/gotrue-js/-/gotrue-js-2.2.3.tgz#8a0a7c882b68657ea9dfceb15de96346b9d1d033" - integrity sha512-2pw29sk2Fv/6CHFd72bIGTUNxI3UsyaEvjdpvgvvFtX7DmVBZPuotmQaZ56/V0sKREBdglt+bwIjOece1lmDCw== - dependencies: - cross-fetch "^3.1.5" - -"@supabase/postgrest-js@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@supabase/postgrest-js/-/postgrest-js-1.1.0.tgz#c820eb51a7c98543de6617f5b782757771f8f6db" - integrity sha512-qkY8TqIu5sJuae8gjeDPjEqPrefzcTraW9PNSVJQHq4TEv98ZmwaXGwBGz0bVL63bqrGA5hqREbQHkANUTXrvA== - dependencies: - cross-fetch "^3.1.5" - -"@supabase/realtime-js@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@supabase/realtime-js/-/realtime-js-2.1.0.tgz#699ac012bc1c721cd6446ad93226085be348e998" - integrity sha512-iplLCofTeYjnx9FIOsIwHLhMp0+7UVyiA4/sCeq40VdOgN9eTIhjEno9Tgh4dJARi4aaXoKfRX1DTxgZaOpPAw== - dependencies: - "@types/phoenix" "^1.5.4" - websocket "^1.0.34" - -"@supabase/storage-js@^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@supabase/storage-js/-/storage-js-2.0.0.tgz#9fceefa31e16b13525b44f886135cf1a5f320389" - integrity sha512-7kXThdRt/xqnOOvZZxBqNkeX1CFNUWc0hYBJtNN/Uvt8ok9hD14foYmroWrHn046wEYFqUrB9U35JYsfTrvltA== - dependencies: - cross-fetch "^3.1.5" - -"@supabase/supabase-js@^2.0.5": - version "2.0.5" - resolved "https://registry.yarnpkg.com/@supabase/supabase-js/-/supabase-js-2.0.5.tgz#0a49b80a5225929e9b8572bccabd931c6ab3b866" - integrity sha512-zewk3U8wSe2UlKqzUk7iNxeMZzgDa5kjosSG4SKuAizIv+xzS+c2EZsYnNtc2byGjah4QT1dw3t91k2E+j+Akg== - dependencies: - "@supabase/functions-js" "^2.0.0" - "@supabase/gotrue-js" "^2.2.2" - "@supabase/postgrest-js" "^1.1.0" - "@supabase/realtime-js" "^2.1.0" - "@supabase/storage-js" "^2.0.0" - cross-fetch "^3.1.5" - -"@swc/helpers@0.4.11": - version "0.4.11" - resolved "https://registry.yarnpkg.com/@swc/helpers/-/helpers-0.4.11.tgz#db23a376761b3d31c26502122f349a21b592c8de" - integrity sha512-rEUrBSGIoSFuYxwBYtlUFMlE2CwGhmW+w9355/5oduSw8e5h2+Tj4UrAGNNgP9915++wj5vkQo0UuOBqOAq4nw== - dependencies: - tslib "^2.4.0" - -"@tailwindcss/typography@^0.5.8": - version "0.5.8" - resolved "https://registry.yarnpkg.com/@tailwindcss/typography/-/typography-0.5.8.tgz#8fb31db5ab0590be6dfa062b1535ac86ad9d12bf" - integrity sha512-xGQEp8KXN8Sd8m6R4xYmwxghmswrd0cPnNI2Lc6fmrC3OojysTBJJGSIVwPV56q4t6THFUK3HJ0EaWwpglSxWw== - dependencies: - lodash.castarray "^4.4.0" - lodash.isplainobject "^4.0.6" - lodash.merge "^4.6.2" - postcss-selector-parser "6.0.10" - -"@tanstack/query-core@4.14.5": - version "4.14.5" - resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-4.14.5.tgz#f8c061a9dd4d7a87f9992db4ddda35c479e93c4a" - integrity sha512-Su1AyrPb6xnm7wXTvpN5tt+B7LViYSh9k04vvuc6+eMVH0HkE9ktZTXibRrTvV83BI1KP5MG7v/k90ne/4zQzw== - -"@tanstack/react-query@^4.14.5": - version "4.14.5" - resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-4.14.5.tgz#e2ff6419ee94426ec5e0eecf9bf77efbeb1a0e4b" - integrity sha512-CuWl/SxSB0zHhHaTja8LNhy9Vdk+vk9IkW3Oiq3lo4gPnTguHmbUzfjEA1x3RfvPeHfPMuq/pYMSbV+CX4aDQA== - dependencies: - "@tanstack/query-core" "4.14.5" - use-sync-external-store "^1.2.0" - -"@testing-library/dom@^8.5.0": - version "8.17.1" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.17.1.tgz#2d7af4ff6dad8d837630fecd08835aee08320ad7" - integrity sha512-KnH2MnJUzmFNPW6RIKfd+zf2Wue8mEKX0M3cpX6aKl5ZXrJM1/c/Pc8c2xDNYQCnJO48Sm5ITbMXgqTr3h4jxQ== - dependencies: - "@babel/code-frame" "^7.10.4" - "@babel/runtime" "^7.12.5" - "@types/aria-query" "^4.2.0" - aria-query "^5.0.0" - chalk "^4.1.0" - dom-accessibility-api "^0.5.9" - lz-string "^1.4.4" - pretty-format "^27.0.2" - -"@testing-library/jest-dom@^5.16.5": - version "5.16.5" - resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz#3912846af19a29b2dbf32a6ae9c31ef52580074e" - integrity sha512-N5ixQ2qKpi5OLYfwQmUb/5mSV9LneAcaUfp32pn4yCnpb8r/Yz0pXFPck21dIicKmi+ta5WRAknkZCfA8refMA== - dependencies: - "@adobe/css-tools" "^4.0.1" - "@babel/runtime" "^7.9.2" - "@types/testing-library__jest-dom" "^5.9.1" - aria-query "^5.0.0" - chalk "^3.0.0" - css.escape "^1.5.1" - dom-accessibility-api "^0.5.6" - lodash "^4.17.15" - redent "^3.0.0" - -"@testing-library/react@^13.4.0": - version "13.4.0" - resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-13.4.0.tgz#6a31e3bf5951615593ad984e96b9e5e2d9380966" - integrity sha512-sXOGON+WNTh3MLE9rve97ftaZukN3oNf2KjDy7YTx6hcTO2uuLHuCGynMDhFwGw/jYf4OJ2Qk0i4i79qMNNkyw== - dependencies: - "@babel/runtime" "^7.12.5" - "@testing-library/dom" "^8.5.0" - "@types/react-dom" "^18.0.0" - -"@tootallnate/once@2": - version "2.0.0" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" - integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== - -"@types/acorn@^4.0.0": - version "4.0.6" - resolved "https://registry.yarnpkg.com/@types/acorn/-/acorn-4.0.6.tgz#d61ca5480300ac41a7d973dd5b84d0a591154a22" - integrity sha512-veQTnWP+1D/xbxVrPC3zHnCZRjSrKfhbMUlEA43iMZLu7EsnTtkJklIuwrCPbOi8YkvDQAiW05VQQFvvz9oieQ== - dependencies: - "@types/estree" "*" - -"@types/aria-query@^4.2.0": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.2.tgz#ed4e0ad92306a704f9fb132a0cfcf77486dbe2bc" - integrity sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig== - -"@types/babel__core@^7.1.14": - version "7.1.19" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.19.tgz#7b497495b7d1b4812bdb9d02804d0576f43ee460" - integrity sha512-WEOTgRsbYkvA/KCsDwVEGkd7WAr1e3g31VHQ8zy5gul/V1qKullU/BU5I68X5v7V3GnB9eotmom4v5a5gjxorw== - dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - "@types/babel__generator" "*" - "@types/babel__template" "*" - "@types/babel__traverse" "*" - -"@types/babel__generator@*": - version "7.6.4" - resolved "https://registry.yarnpkg.com/@types/babel__generator/-/babel__generator-7.6.4.tgz#1f20ce4c5b1990b37900b63f050182d28c2439b7" - integrity sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg== - dependencies: - "@babel/types" "^7.0.0" - -"@types/babel__template@*": - version "7.4.1" - resolved "https://registry.yarnpkg.com/@types/babel__template/-/babel__template-7.4.1.tgz#3d1a48fd9d6c0edfd56f2ff578daed48f36c8969" - integrity sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g== - dependencies: - "@babel/parser" "^7.1.0" - "@babel/types" "^7.0.0" - -"@types/babel__traverse@*", "@types/babel__traverse@^7.0.6": - version "7.18.0" - resolved "https://registry.yarnpkg.com/@types/babel__traverse/-/babel__traverse-7.18.0.tgz#8134fd78cb39567465be65b9fdc16d378095f41f" - integrity sha512-v4Vwdko+pgymgS+A2UIaJru93zQd85vIGWObM5ekZNdXCKtDYqATlEYnWgfo86Q6I1Lh0oXnksDnMU1cwmlPDw== - dependencies: - "@babel/types" "^7.3.0" - -"@types/debug@^4.0.0": - version "4.1.7" - resolved "https://registry.yarnpkg.com/@types/debug/-/debug-4.1.7.tgz#7cc0ea761509124709b8b2d1090d8f6c17aadb82" - integrity sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg== - dependencies: - "@types/ms" "*" - -"@types/estree-jsx@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/estree-jsx/-/estree-jsx-1.0.0.tgz#7bfc979ab9f692b492017df42520f7f765e98df1" - integrity sha512-3qvGd0z8F2ENTGr/GG1yViqfiKmRfrXVx5sJyHGFu3z7m5g5utCQtGp/g29JnjflhtQJBv1WDQukHiT58xPcYQ== - dependencies: - "@types/estree" "*" - -"@types/estree@*", "@types/estree@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.0.tgz#5fb2e536c1ae9bf35366eed879e827fa59ca41c2" - integrity sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ== - -"@types/graceful-fs@^4.1.3": - version "4.1.5" - resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.5.tgz#21ffba0d98da4350db64891f92a9e5db3cdb4e15" - integrity sha512-anKkLmZZ+xm4p8JWBf4hElkM4XR+EZeA2M9BAkkTldmcyDY4mbdIJnRghDJH3Ov5ooY7/UAoENtmdMSkaAd7Cw== - dependencies: - "@types/node" "*" - -"@types/hast@^2.0.0": - version "2.3.4" - resolved "https://registry.yarnpkg.com/@types/hast/-/hast-2.3.4.tgz#8aa5ef92c117d20d974a82bdfb6a648b08c0bafc" - integrity sha512-wLEm0QvaoawEDoTRwzTXp4b4jpwiJDvR5KMnFnVodm3scufTlBOWRD6N1OBf9TZMhjlNsSfcO5V+7AF4+Vy+9g== - dependencies: - "@types/unist" "*" - -"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz#8467d4b3c087805d63580480890791277ce35c44" - integrity sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g== - -"@types/istanbul-lib-report@*": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#c14c24f18ea8190c118ee7562b7ff99a36552686" - integrity sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg== - dependencies: - "@types/istanbul-lib-coverage" "*" - -"@types/istanbul-reports@^3.0.0": - version "3.0.1" - resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz#9153fe98bba2bd565a63add9436d6f0d7f8468ff" - integrity sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw== - dependencies: - "@types/istanbul-lib-report" "*" - -"@types/jest@*": - version "28.1.6" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-28.1.6.tgz#d6a9cdd38967d2d746861fb5be6b120e38284dd4" - integrity sha512-0RbGAFMfcBJKOmqRazM8L98uokwuwD5F8rHrv/ZMbrZBwVOWZUyPG6VFNscjYr/vjM3Vu4fRrCPbOs42AfemaQ== - dependencies: - jest-matcher-utils "^28.0.0" - pretty-format "^28.0.0" - -"@types/jest@^29.2.2": - version "29.2.2" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-29.2.2.tgz#874e7dc6702fa6a3fe6107792aa98636dcc480b4" - integrity sha512-og1wAmdxKoS71K2ZwSVqWPX6OVn3ihZ6ZT2qvZvZQm90lJVDyXIjYcu4Khx2CNIeaFv12rOU/YObOsI3VOkzog== - dependencies: - expect "^29.0.0" - pretty-format "^29.0.0" - -"@types/js-cookie@^2.2.6": - version "2.2.7" - resolved "https://registry.yarnpkg.com/@types/js-cookie/-/js-cookie-2.2.7.tgz#226a9e31680835a6188e887f3988e60c04d3f6a3" - integrity sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA== - -"@types/js-yaml@^4.0.0": - version "4.0.5" - resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.5.tgz#738dd390a6ecc5442f35e7f03fa1431353f7e138" - integrity sha512-FhpRzf927MNQdRZP0J5DLIdTXhjLYzeUTmLAu69mnVksLH9CJY3IuSeEgbKUki7GQZm0WqDkGzyxju2EZGD2wA== - -"@types/jsdom@^20.0.0": - version "20.0.0" - resolved "https://registry.yarnpkg.com/@types/jsdom/-/jsdom-20.0.0.tgz#4414fb629465167f8b7b3804b9e067bdd99f1791" - integrity sha512-YfAchFs0yM1QPDrLm2VHe+WHGtqms3NXnXAMolrgrVP6fgBHHXy1ozAbo/dFtPNtZC/m66bPiCTWYmqp1F14gA== - dependencies: - "@types/node" "*" - "@types/tough-cookie" "*" - parse5 "^7.0.0" - -"@types/json5@^0.0.29": - version "0.0.29" - resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee" - integrity sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ== - -"@types/mdast@^3.0.0": - version "3.0.10" - resolved "https://registry.yarnpkg.com/@types/mdast/-/mdast-3.0.10.tgz#4724244a82a4598884cbbe9bcfd73dff927ee8af" - integrity sha512-W864tg/Osz1+9f4lrGTZpCSO5/z4608eUp19tbozkq2HJK6i3z1kT0H9tlADXuYIb1YYOBByU4Jsqkk75q48qA== - dependencies: - "@types/unist" "*" - -"@types/mdx@^2.0.0": - version "2.0.2" - resolved "https://registry.yarnpkg.com/@types/mdx/-/mdx-2.0.2.tgz#64be19baddba4323ae7893e077e98759316fe279" - integrity sha512-mJGfgj4aWpiKb8C0nnJJchs1sHBHn0HugkVfqqyQi7Wn6mBRksLeQsPOFvih/Pu8L1vlDzfe/LidhVHBeUk3aQ== - -"@types/ms@*": - version "0.7.31" - resolved "https://registry.yarnpkg.com/@types/ms/-/ms-0.7.31.tgz#31b7ca6407128a3d2bbc27fe2d21b345397f6197" - integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== - -"@types/node@*": - version "18.7.3" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.3.tgz#432c89796eab539b7a30b7b8801a727b585238a4" - integrity sha512-LJgzOEwWuMTBxHzgBR/fhhBOWrvBjvO+zPteUgbbuQi80rYIZHrk1mNbRUqPZqSLP2H7Rwt1EFLL/tNLD1Xx/w== - -"@types/node@>=8.1.0": - version "18.7.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.7.6.tgz#31743bc5772b6ac223845e18c3fc26f042713c83" - integrity sha512-EdxgKRXgYsNITy5mjjXjVE/CS8YENSdhiagGrLqjG0pvA2owgJ6i4l7wy/PFZGC0B1/H20lWKN7ONVDNYDZm7A== - -"@types/node@^18.11.9": - version "18.11.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.9.tgz#02d013de7058cea16d36168ef2fc653464cfbad4" - integrity sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg== - -"@types/phoenix@^1.5.4": - version "1.5.4" - resolved "https://registry.yarnpkg.com/@types/phoenix/-/phoenix-1.5.4.tgz#c08a1da6d7b4e365f6a1fe1ff9aada55f5356d24" - integrity sha512-L5eZmzw89eXBKkiqVBcJfU1QGx9y+wurRIEgt0cuLH0hwNtVUxtx+6cu0R2STwWj468sjXyBYPYDtGclUd1kjQ== - -"@types/prettier@^2.1.5": - version "2.7.0" - resolved "https://registry.yarnpkg.com/@types/prettier/-/prettier-2.7.0.tgz#ea03e9f0376a4446f44797ca19d9c46c36e352dc" - integrity sha512-RI1L7N4JnW5gQw2spvL7Sllfuf1SaHdrZpCHiBlCXjIlufi1SMNnbu2teze3/QE67Fg2tBlH7W+mi4hVNk4p0A== - -"@types/prop-types@*": - version "15.7.5" - resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.5.tgz#5f19d2b85a98e9558036f6a3cacc8819420f05cf" - integrity sha512-JCB8C6SnDoQf0cNycqd/35A7MjcnK+ZTqE7judS6o7utxUCg6imJg3QK2qzHKszlTjcj2cn+NwMB2i96ubpj7w== - -"@types/react-dom@^18.0.0": - version "18.0.6" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.6.tgz#36652900024842b74607a17786b6662dd1e103a1" - integrity sha512-/5OFZgfIPSwy+YuIBP/FgJnQnsxhZhjjrnxudMddeblOouIodEQ75X14Rr4wGSG/bknL+Omy9iWlLo1u/9GzAA== - dependencies: - "@types/react" "*" - -"@types/react-dom@^18.0.8": - version "18.0.8" - resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.0.8.tgz#d2606d855186cd42cc1b11e63a71c39525441685" - integrity sha512-C3GYO0HLaOkk9dDAz3Dl4sbe4AKUGTCfFIZsz3n/82dPNN8Du533HzKatDxeUYWu24wJgMP1xICqkWk1YOLOIw== - dependencies: - "@types/react" "*" - -"@types/react@*": - version "18.0.17" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.17.tgz#4583d9c322d67efe4b39a935d223edcc7050ccf4" - integrity sha512-38ETy4tL+rn4uQQi7mB81G7V1g0u2ryquNmsVIOKUAEIDK+3CUjZ6rSRpdvS99dNBnkLFL83qfmtLacGOTIhwQ== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "*" - csstype "^3.0.2" - -"@types/react@>=16": - version "18.0.21" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.21.tgz#b8209e9626bb00a34c76f55482697edd2b43cc67" - integrity sha512-7QUCOxvFgnD5Jk8ZKlUAhVcRj7GuJRjnjjiY/IUBWKgOlnvDvTMLD4RTF7NPyVmbRhNrbomZiOepg7M/2Kj1mA== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "*" - csstype "^3.0.2" - -"@types/react@^18.0.25": - version "18.0.25" - resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.25.tgz#8b1dcd7e56fe7315535a4af25435e0bb55c8ae44" - integrity sha512-xD6c0KDT4m7n9uD4ZHi02lzskaiqcBxf4zi+tXZY98a04wvc0hi/TcCPC2FOESZi51Nd7tlUeOJY8RofL799/g== - dependencies: - "@types/prop-types" "*" - "@types/scheduler" "*" - csstype "^3.0.2" - -"@types/scheduler@*": - version "0.16.2" - resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" - integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== - -"@types/stack-utils@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.1.tgz#20f18294f797f2209b5f65c8e3b5c8e8261d127c" - integrity sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw== - -"@types/testing-library__jest-dom@^5.9.1": - version "5.14.5" - resolved "https://registry.yarnpkg.com/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.14.5.tgz#d113709c90b3c75fdb127ec338dad7d5f86c974f" - integrity sha512-SBwbxYoyPIvxHbeHxTZX2Pe/74F/tX2/D3mMvzabdeJ25bBojfW0TyB8BHrbq/9zaaKICJZjLP+8r6AeZMFCuQ== - dependencies: - "@types/jest" "*" - -"@types/tough-cookie@*": - version "4.0.2" - resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397" - integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw== - -"@types/unist@*", "@types/unist@^2.0.0": - version "2.0.6" - resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.6.tgz#250a7b16c3b91f672a24552ec64678eeb1d3a08d" - integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== - -"@types/yargs-parser@*": - version "21.0.0" - resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" - integrity sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA== - -"@types/yargs@^17.0.8": - version "17.0.11" - resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-17.0.11.tgz#5e10ca33e219807c0eee0f08b5efcba9b6a42c06" - integrity sha512-aB4y9UDUXTSMxmM4MH+YnuR0g5Cph3FLQBoWoMB21DSvFVAxRVEHEMx3TLh+zUZYMCQtKiqazz0Q4Rre31f/OA== - dependencies: - "@types/yargs-parser" "*" - -"@types/yoga-layout@1.9.2": - version "1.9.2" - resolved "https://registry.yarnpkg.com/@types/yoga-layout/-/yoga-layout-1.9.2.tgz#efaf9e991a7390dc081a0b679185979a83a9639a" - integrity sha512-S9q47ByT2pPvD65IvrWp7qppVMpk9WGMbVq9wbWZOHg6tnXSD4vyhao6nOSBwwfDdV2p3Kx9evA9vI+XWTfDvw== - -"@typescript-eslint/parser@^5.21.0": - version "5.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.33.0.tgz#26ec3235b74f0667414613727cb98f9b69dc5383" - integrity sha512-cgM5cJrWmrDV2KpvlcSkelTBASAs1mgqq+IUGKJvFxWrapHpaRy5EXPQz9YaKF3nZ8KY18ILTiVpUtbIac86/w== - dependencies: - "@typescript-eslint/scope-manager" "5.33.0" - "@typescript-eslint/types" "5.33.0" - "@typescript-eslint/typescript-estree" "5.33.0" - debug "^4.3.4" - -"@typescript-eslint/scope-manager@5.33.0": - version "5.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.33.0.tgz#509d7fa540a2c58f66bdcfcf278a3fa79002e18d" - integrity sha512-/Jta8yMNpXYpRDl8EwF/M8It2A9sFJTubDo0ATZefGXmOqlaBffEw0ZbkbQ7TNDK6q55NPHFshGBPAZvZkE8Pw== - dependencies: - "@typescript-eslint/types" "5.33.0" - "@typescript-eslint/visitor-keys" "5.33.0" - -"@typescript-eslint/types@5.33.0": - version "5.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.33.0.tgz#d41c584831805554b063791338b0220b613a275b" - integrity sha512-nIMt96JngB4MYFYXpZ/3ZNU4GWPNdBbcB5w2rDOCpXOVUkhtNlG2mmm8uXhubhidRZdwMaMBap7Uk8SZMU/ppw== - -"@typescript-eslint/typescript-estree@5.33.0": - version "5.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.33.0.tgz#02d9c9ade6f4897c09e3508c27de53ad6bfa54cf" - integrity sha512-tqq3MRLlggkJKJUrzM6wltk8NckKyyorCSGMq4eVkyL5sDYzJJcMgZATqmF8fLdsWrW7OjjIZ1m9v81vKcaqwQ== - dependencies: - "@typescript-eslint/types" "5.33.0" - "@typescript-eslint/visitor-keys" "5.33.0" - debug "^4.3.4" - globby "^11.1.0" - is-glob "^4.0.3" - semver "^7.3.7" - tsutils "^3.21.0" - -"@typescript-eslint/visitor-keys@5.33.0": - version "5.33.0" - resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.33.0.tgz#fbcbb074e460c11046e067bc3384b5d66b555484" - integrity sha512-/XsqCzD4t+Y9p5wd9HZiptuGKBlaZO5showwqODii5C0nZawxWLF+Q6k5wYHBrQv96h6GYKyqqMHCSTqta8Kiw== - dependencies: - "@typescript-eslint/types" "5.33.0" - eslint-visitor-keys "^3.3.0" - -"@vercel/og@^0.0.20": - version "0.0.20" - resolved "https://registry.yarnpkg.com/@vercel/og/-/og-0.0.20.tgz#fe586d153e492edd87fe2bc2671ba223072bd47a" - integrity sha512-089P+TfqWz0xBxjOvOhkZIDDtfrLcye94H4IZ+SqxoGPWpNGXaBvRJER/z5SoJxJRcCAL8tPiK5zdjRskM6tLw== - dependencies: - "@resvg/resvg-wasm" "2.0.0-alpha.4" - satori "0.0.43" - yoga-wasm-web "0.1.2" - -"@xobotyi/scrollbar-width@^1.9.5": - version "1.9.5" - resolved "https://registry.yarnpkg.com/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz#80224a6919272f405b87913ca13b92929bdf3c4d" - integrity sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ== - -abab@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" - integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== - -acorn-globals@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/acorn-globals/-/acorn-globals-6.0.0.tgz#46cdd39f0f8ff08a876619b55f5ac8a6dc770b45" - integrity sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg== - dependencies: - acorn "^7.1.1" - acorn-walk "^7.1.1" - -acorn-jsx@^5.0.0, acorn-jsx@^5.3.2: - version "5.3.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" - integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== - -acorn-node@^1.8.2: - version "1.8.2" - resolved "https://registry.yarnpkg.com/acorn-node/-/acorn-node-1.8.2.tgz#114c95d64539e53dede23de8b9d96df7c7ae2af8" - integrity sha512-8mt+fslDufLYntIoPAaIMUe/lrbrehIiwmR3t2k9LljIzoigEPF27eLk2hy8zSGzmR/ogr7zbRKINMo1u0yh5A== - dependencies: - acorn "^7.0.0" - acorn-walk "^7.0.0" - xtend "^4.0.2" - -acorn-walk@^7.0.0, acorn-walk@^7.1.1: - version "7.2.0" - resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-7.2.0.tgz#0de889a601203909b0fbe07b8938dc21d2e967bc" - integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== - -acorn@^7.0.0, acorn@^7.1.1: - version "7.4.1" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" - integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== - -acorn@^8.0.0, acorn@^8.7.1, acorn@^8.8.0: - version "8.8.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.8.0.tgz#88c0187620435c7f6015803f5539dae05a9dbea8" - integrity sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w== - -agent-base@6: - version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" - integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== - dependencies: - debug "4" - -ajv@^6.10.0, ajv@^6.12.4: - version "6.12.6" - resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" - integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== - dependencies: - fast-deep-equal "^3.1.1" - fast-json-stable-stringify "^2.0.0" - json-schema-traverse "^0.4.1" - uri-js "^4.2.2" - -ansi-escapes@^4.2.1: - version "4.3.2" - resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" - integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== - dependencies: - type-fest "^0.21.3" - -ansi-regex@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" - integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== - -ansi-styles@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d" - integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA== - dependencies: - color-convert "^1.9.0" - -ansi-styles@^4.0.0, ansi-styles@^4.1.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -ansi-styles@^5.0.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" - integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== - -anymatch@^3.0.3, anymatch@~3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716" - integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -arg@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" - integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg== - -argparse@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" - integrity sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg== - dependencies: - sprintf-js "~1.0.2" - -argparse@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" - integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== - -aria-query@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b" - integrity sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA== - dependencies: - "@babel/runtime" "^7.10.2" - "@babel/runtime-corejs3" "^7.10.2" - -aria-query@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.0.0.tgz#210c21aaf469613ee8c9a62c7f86525e058db52c" - integrity sha512-V+SM7AbUwJ+EBnB8+DXs0hPZHO0W6pqBcc0dW90OwtVG02PswOu/teuARoLQjdDOH+t9pJgGnW5/Qmouf3gPJg== - -array-includes@^3.1.4, array-includes@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.5.tgz#2c320010db8d31031fd2a5f6b3bbd4b1aad31bdb" - integrity sha512-iSDYZMMyTPkiFasVqfuAQnWAYcvO/SeBSCGKePoEthjp4LEMTe4uLc7b025o4jAZpHhihh8xPo99TNWUWWkGDQ== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.19.5" - get-intrinsic "^1.1.1" - is-string "^1.0.7" - -array-union@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" - integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== - -array.prototype.flat@^1.2.5: - version "1.3.0" - resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.3.0.tgz#0b0c1567bf57b38b56b4c97b8aa72ab45e4adc7b" - integrity sha512-12IUEkHsAhA4DY5s0FPgNXIdc8VRSqD9Zp78a5au9abH/SOBrsp082JOWFNTjkMozh8mqcdiKuaLGhPeYztxSw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.2" - es-shim-unscopables "^1.0.0" - -array.prototype.flatmap@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/array.prototype.flatmap/-/array.prototype.flatmap-1.3.0.tgz#a7e8ed4225f4788a70cd910abcf0791e76a5534f" - integrity sha512-PZC9/8TKAIxcWKdyeb77EzULHPrIX/tIZebLJUQOMR1OwYosT8yggdfWScfTBCDj5utONvOuPQQumYsU2ULbkg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.2" - es-shim-unscopables "^1.0.0" - -ast-types-flow@^0.0.7: - version "0.0.7" - resolved "https://registry.yarnpkg.com/ast-types-flow/-/ast-types-flow-0.0.7.tgz#f70b735c6bca1a5c9c22d982c3e39e7feba3bdad" - integrity sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag== - -astring@^1.8.0: - version "1.8.3" - resolved "https://registry.yarnpkg.com/astring/-/astring-1.8.3.tgz#1a0ae738c7cc558f8e5ddc8e3120636f5cebcb85" - integrity sha512-sRpyiNrx2dEYIMmUXprS8nlpRg2Drs8m9ElX9vVEXaCB4XEAJhKfs7IcX0IwShjuOAjLR6wzIrgoptz1n19i1A== - -asynckit@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" - integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== - -autoprefixer@^10.4.13: - version "10.4.13" - resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.13.tgz#b5136b59930209a321e9fa3dca2e7c4d223e83a8" - integrity sha512-49vKpMqcZYsJjwotvt4+h/BCjJVnhGwcLpDt5xkcaOG3eLrG/HUYLagrihYsQ+qrIBgIzX1Rw7a6L8I/ZA1Atg== - dependencies: - browserslist "^4.21.4" - caniuse-lite "^1.0.30001426" - fraction.js "^4.2.0" - normalize-range "^0.1.2" - picocolors "^1.0.0" - postcss-value-parser "^4.2.0" - -axe-core@^4.4.3: - version "4.4.3" - resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.3.tgz#11c74d23d5013c0fa5d183796729bc3482bd2f6f" - integrity sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w== - -axobject-query@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be" - integrity sha512-Td525n+iPOOyUQIeBfcASuG6uJsDOITl7Mds5gFyerkWiX7qhUTdYUBlSgNMyVqtSJqwpt1kXGLdUt6SykLMRA== - -babel-jest@^29.3.0: - version "29.3.0" - resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-29.3.0.tgz#31fd7ef97dd6a77ddd1b4ab1f8cf77c29e20bb5c" - integrity sha512-LzQWdGm6hUugVeyGpIKI/T4SVT+PgAA5WFPqBDbneK7C/PqfckNb0tc4KvcKXq/PLA1yY6wTvB8Bc/REQdUxFg== - dependencies: - "@jest/transform" "^29.3.0" - "@types/babel__core" "^7.1.14" - babel-plugin-istanbul "^6.1.1" - babel-preset-jest "^29.2.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - slash "^3.0.0" - -babel-plugin-istanbul@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz#fa88ec59232fd9b4e36dbbc540a8ec9a9b47da73" - integrity sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA== - dependencies: - "@babel/helper-plugin-utils" "^7.0.0" - "@istanbuljs/load-nyc-config" "^1.0.0" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-instrument "^5.0.4" - test-exclude "^6.0.0" - -babel-plugin-jest-hoist@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.2.0.tgz#23ee99c37390a98cfddf3ef4a78674180d823094" - integrity sha512-TnspP2WNiR3GLfCsUNHqeXw0RoQ2f9U5hQ5L3XFpwuO8htQmSrhh8qsB6vi5Yi8+kuynN1yjDjQsPfkebmB6ZA== - dependencies: - "@babel/template" "^7.3.3" - "@babel/types" "^7.3.3" - "@types/babel__core" "^7.1.14" - "@types/babel__traverse" "^7.0.6" - -babel-preset-current-node-syntax@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" - integrity sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ== - dependencies: - "@babel/plugin-syntax-async-generators" "^7.8.4" - "@babel/plugin-syntax-bigint" "^7.8.3" - "@babel/plugin-syntax-class-properties" "^7.8.3" - "@babel/plugin-syntax-import-meta" "^7.8.3" - "@babel/plugin-syntax-json-strings" "^7.8.3" - "@babel/plugin-syntax-logical-assignment-operators" "^7.8.3" - "@babel/plugin-syntax-nullish-coalescing-operator" "^7.8.3" - "@babel/plugin-syntax-numeric-separator" "^7.8.3" - "@babel/plugin-syntax-object-rest-spread" "^7.8.3" - "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" - "@babel/plugin-syntax-optional-chaining" "^7.8.3" - "@babel/plugin-syntax-top-level-await" "^7.8.3" - -babel-preset-jest@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-29.2.0.tgz#3048bea3a1af222e3505e4a767a974c95a7620dc" - integrity sha512-z9JmMJppMxNv8N7fNRHvhMg9cvIkMxQBXgFkane3yKVEvEOP+kB50lk8DFRvF9PGqbyXxlmebKWhuDORO8RgdA== - dependencies: - babel-plugin-jest-hoist "^29.2.0" - babel-preset-current-node-syntax "^1.0.0" - -bail@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/bail/-/bail-2.0.2.tgz#d26f5cd8fe5d6f832a31517b9f7c356040ba6d5d" - integrity sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw== - -balanced-match@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" - integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== - -binary-extensions@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" - integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== - -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - -braces@^3.0.2, braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== - dependencies: - fill-range "^7.0.1" - -browser-process-hrtime@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" - integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== - -browserslist@^4.20.2: - version "4.21.3" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.3.tgz#5df277694eb3c48bc5c4b05af3e8b7e09c5a6d1a" - integrity sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ== - dependencies: - caniuse-lite "^1.0.30001370" - electron-to-chromium "^1.4.202" - node-releases "^2.0.6" - update-browserslist-db "^1.0.5" - -browserslist@^4.21.4: - version "4.21.4" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.4.tgz#e7496bbc67b9e39dd0f98565feccdcb0d4ff6987" - integrity sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw== - dependencies: - caniuse-lite "^1.0.30001400" - electron-to-chromium "^1.4.251" - node-releases "^2.0.6" - update-browserslist-db "^1.0.9" - -bs-logger@0.x: - version "0.2.6" - resolved "https://registry.yarnpkg.com/bs-logger/-/bs-logger-0.2.6.tgz#eb7d365307a72cf974cc6cda76b68354ad336bd8" - integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== - dependencies: - fast-json-stable-stringify "2.x" - -bser@2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/bser/-/bser-2.1.1.tgz#e6787da20ece9d07998533cfd9de6f5c38f4bc05" - integrity sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ== - dependencies: - node-int64 "^0.4.0" - -buffer-from@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" - integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== - -bufferutil@^4.0.1: - version "4.0.6" - resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.6.tgz#ebd6c67c7922a0e902f053e5d8be5ec850e48433" - integrity sha512-jduaYOYtnio4aIAyc6UbvPCVcgq7nYpVnucyxr6eCYg/Woad9Hf/oxxBRDnGGjPfjUm6j5O/uBWhIu4iLebFaw== - dependencies: - node-gyp-build "^4.3.0" - -call-bind@^1.0.0, call-bind@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c" - integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA== - dependencies: - function-bind "^1.1.1" - get-intrinsic "^1.0.2" - -callsites@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" - integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== - -camelcase-css@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/camelcase-css/-/camelcase-css-2.0.1.tgz#ee978f6947914cc30c6b44741b6ed1df7f043fd5" - integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA== - -camelcase@^5.3.1: - version "5.3.1" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" - integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== - -camelcase@^6.2.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" - integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== - -camelize@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/camelize/-/camelize-1.0.1.tgz#89b7e16884056331a35d6b5ad064332c91daa6c3" - integrity sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ== - -caniuse-lite@^1.0.30001370: - version "1.0.30001375" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001375.tgz#8e73bc3d1a4c800beb39f3163bf0190d7e5d7672" - integrity sha512-kWIMkNzLYxSvnjy0hL8w1NOaWNr2rn39RTAVyIwcw8juu60bZDWiF1/loOYANzjtJmy6qPgNmn38ro5Pygagdw== - -caniuse-lite@^1.0.30001400, caniuse-lite@^1.0.30001406: - version "1.0.30001410" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001410.tgz#b5a86366fbbf439d75dd3db1d21137a73e829f44" - integrity sha512-QoblBnuE+rG0lc3Ur9ltP5q47lbguipa/ncNMyyGuqPk44FxbScWAeEO+k5fSQ8WekdAK4mWqNs1rADDAiN5xQ== - -caniuse-lite@^1.0.30001426: - version "1.0.30001431" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001431.tgz#e7c59bd1bc518fae03a4656be442ce6c4887a795" - integrity sha512-zBUoFU0ZcxpvSt9IU66dXVT/3ctO1cy4y9cscs1szkPlcWb6pasYM144GqrUygUbT+k7cmUCW61cvskjcv0enQ== - -ccount@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/ccount/-/ccount-2.0.1.tgz#17a3bf82302e0870d6da43a01311a8bc02a3ecf5" - integrity sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg== - -chalk@^2.0.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" - integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== - dependencies: - ansi-styles "^3.2.1" - escape-string-regexp "^1.0.5" - supports-color "^5.3.0" - -chalk@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4" - integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -chalk@^4.0.0, chalk@^4.1.0: - version "4.1.2" - resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" - integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== - dependencies: - ansi-styles "^4.1.0" - supports-color "^7.1.0" - -char-regex@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf" - integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw== - -character-entities-html4@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz#1f1adb940c971a4b22ba39ddca6b618dc6e56b2b" - integrity sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA== - -character-entities-legacy@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz#76bc83a90738901d7bc223a9e93759fdd560125b" - integrity sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ== - -character-entities@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-2.0.2.tgz#2d09c2e72cd9523076ccb21157dff66ad43fcc22" - integrity sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ== - -character-reference-invalid@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz#85c66b041e43b47210faf401278abf808ac45cb9" - integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw== - -chokidar@^3.5.3: - version "3.5.3" - resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" - integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== - dependencies: - anymatch "~3.1.2" - braces "~3.0.2" - glob-parent "~5.1.2" - is-binary-path "~2.1.0" - is-glob "~4.0.1" - normalize-path "~3.0.0" - readdirp "~3.6.0" - optionalDependencies: - fsevents "~2.3.2" - -ci-info@^3.2.0: - version "3.3.2" - resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.2.tgz#6d2967ffa407466481c6c90b6e16b3098f080128" - integrity sha512-xmDt/QIAdeZ9+nfdPsaBCpMvHNLFiLdjj59qjqn+6iPe6YmHGQ35sBnQ8uslRBXFmXkiZQOJRjvQeoGppoTjjg== - -cjs-module-lexer@^1.0.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/cjs-module-lexer/-/cjs-module-lexer-1.2.2.tgz#9f84ba3244a512f3a54e5277e8eef4c489864e40" - integrity sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA== - -classnames@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.3.2.tgz#351d813bf0137fcc6a76a16b88208d2560a0d924" - integrity sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw== - -client-only@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/client-only/-/client-only-0.0.1.tgz#38bba5d403c41ab150bff64a95c85013cf73bca1" - integrity sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA== - -cliui@^7.0.2: - version "7.0.4" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" - integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== - dependencies: - string-width "^4.2.0" - strip-ansi "^6.0.0" - wrap-ansi "^7.0.0" - -clsx@^1.1.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" - integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== - -co@^4.6.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" - integrity sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ== - -collect-v8-coverage@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz#cc2c8e94fc18bbdffe64d6534570c8a673b27f59" - integrity sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg== - -color-convert@^1.9.0: - version "1.9.3" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" - integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg== - dependencies: - color-name "1.1.3" - -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" - integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw== - -color-name@^1.0.0, color-name@^1.1.4, color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - -color-string@^1.9.0: - version "1.9.1" - resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4" - integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg== - dependencies: - color-name "^1.0.0" - simple-swizzle "^0.2.2" - -color@^4.2: - version "4.2.3" - resolved "https://registry.yarnpkg.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a" - integrity sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A== - dependencies: - color-convert "^2.0.1" - color-string "^1.9.0" - -combined-stream@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" - integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== - dependencies: - delayed-stream "~1.0.0" - -comma-separated-tokens@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-2.0.2.tgz#d4c25abb679b7751c880be623c1179780fe1dd98" - integrity sha512-G5yTt3KQN4Yn7Yk4ed73hlZ1evrFKXeUW3086p3PRFNp7m2vIjI6Pg+Kgb+oyzhd9F2qdcoj67+y3SdxL5XWsg== - -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== - -convert-source-map@^1.6.0, convert-source-map@^1.7.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-1.8.0.tgz#f3373c32d21b4d780dd8004514684fb791ca4369" - integrity sha512-+OQdjP49zViI/6i7nIJpA8rAl4sV/JdPfU9nZs3VqOwGIgizICvuN2ru6fMd+4llL0tar18UYJXfZ/TWtmhUjA== - dependencies: - safe-buffer "~5.1.1" - -convert-source-map@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/convert-source-map/-/convert-source-map-2.0.0.tgz#4b560f649fc4e918dd0ab75cf4961e8bc882d82a" - integrity sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg== - -copy-to-clipboard@^3.3.1: - version "3.3.2" - resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.2.tgz#5b263ec2366224b100181dded7ce0579b340c107" - integrity sha512-Vme1Z6RUDzrb6xAI7EZlVZ5uvOk2F//GaxKUxajDqm9LhOVM1inxNAD2vy+UZDYsd0uyA9s7b3/FVZPSxqrCfg== - dependencies: - toggle-selection "^1.0.6" - -core-js-pure@^3.20.2: - version "3.24.1" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.24.1.tgz#8839dde5da545521bf282feb7dc6d0b425f39fd3" - integrity sha512-r1nJk41QLLPyozHUUPmILCEMtMw24NG4oWK6RbsDdjzQgg9ZvrUsPBj1MnG0wXXp1DCDU6j+wUvEmBSrtRbLXg== - -cross-fetch@^3.0.4, cross-fetch@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.5.tgz#e1389f44d9e7ba767907f7af8454787952ab534f" - integrity sha512-lvb1SBsI0Z7GDwmuid+mU3kWVBwTVUbe7S0H52yaaAdQOXq2YktTCZdlAcNKFzE6QtRz0snpw9bNiPeOIkkQvw== - dependencies: - node-fetch "2.6.7" - -cross-spawn@^7.0.2, cross-spawn@^7.0.3: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -css-background-parser@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/css-background-parser/-/css-background-parser-0.1.0.tgz#48a17f7fe6d4d4f1bca3177ddf16c5617950741b" - integrity sha512-2EZLisiZQ+7m4wwur/qiYJRniHX4K5Tc9w93MT3AS0WS1u5kaZ4FKXlOTBhOjc+CgEgPiGY+fX1yWD8UwpEqUA== - -css-box-shadow@1.0.0-3: - version "1.0.0-3" - resolved "https://registry.yarnpkg.com/css-box-shadow/-/css-box-shadow-1.0.0-3.tgz#9eaeb7140947bf5d649fc49a19e4bbaa5f602713" - integrity sha512-9jaqR6e7Ohds+aWwmhe6wILJ99xYQbfmK9QQB9CcMjDbTxPZjwEmUQpU91OG05Xgm8BahT5fW+svbsQGjS/zPg== - -css-color-keywords@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/css-color-keywords/-/css-color-keywords-1.0.0.tgz#fea2616dc676b2962686b3af8dbdbe180b244e05" - integrity sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg== - -css-in-js-utils@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/css-in-js-utils/-/css-in-js-utils-2.0.1.tgz#3b472b398787291b47cfe3e44fecfdd9e914ba99" - integrity sha512-PJF0SpJT+WdbVVt0AOYp9C8GnuruRlL/UFW7932nLWmFLQTaWEzTBQEx7/hn4BuV+WON75iAViSUJLiU3PKbpA== - dependencies: - hyphenate-style-name "^1.0.2" - isobject "^3.0.1" - -css-selector-tokenizer@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz#88267ef6238e64f2215ea2764b3e2cf498b845dd" - integrity sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg== - dependencies: - cssesc "^3.0.0" - fastparse "^1.1.2" - -css-to-react-native@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/css-to-react-native/-/css-to-react-native-3.0.0.tgz#62dbe678072a824a689bcfee011fc96e02a7d756" - integrity sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ== - dependencies: - camelize "^1.0.0" - css-color-keywords "^1.0.0" - postcss-value-parser "^4.0.2" - -css-tree@^1.1.2: - version "1.1.3" - resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-1.1.3.tgz#eb4870fb6fd7707327ec95c2ff2ab09b5e8db91d" - integrity sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q== - dependencies: - mdn-data "2.0.14" - source-map "^0.6.1" - -css.escape@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/css.escape/-/css.escape-1.5.1.tgz#42e27d4fa04ae32f931a4b4d4191fa9cddee97cb" - integrity sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg== - -cssesc@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee" - integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg== - -cssom@^0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.5.0.tgz#d254fa92cd8b6fbd83811b9fbaed34663cc17c36" - integrity sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw== - -cssom@~0.3.6: - version "0.3.8" - resolved "https://registry.yarnpkg.com/cssom/-/cssom-0.3.8.tgz#9f1276f5b2b463f2114d3f2c75250af8c1a36f4a" - integrity sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg== - -cssstyle@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/cssstyle/-/cssstyle-2.3.0.tgz#ff665a0ddbdc31864b09647f34163443d90b0852" - integrity sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A== - dependencies: - cssom "~0.3.6" - -csstype@^3.0.2, csstype@^3.0.6: - version "3.1.0" - resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.0.tgz#4ddcac3718d787cf9df0d1b7d15033925c8f29f2" - integrity sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA== - -d@1, d@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" - integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== - dependencies: - es5-ext "^0.10.50" - type "^1.0.1" - -daisyui@^2.38.1: - version "2.38.1" - resolved "https://registry.yarnpkg.com/daisyui/-/daisyui-2.38.1.tgz#28a3303a1832f41e9804d76ec769335ac1248372" - integrity sha512-fHAVi+bT3H9QCNM7LNEH1EMPT0IwqufuW2RNhTJZorX3TftB1bWW437BVmSt70AZH72Xvp0mVxwVS3ChSKSj0w== - dependencies: - color "^4.2" - css-selector-tokenizer "^0.8.0" - postcss-js "^4.0.0" - tailwindcss "^3" - -damerau-levenshtein@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" - integrity sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA== - -data-urls@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/data-urls/-/data-urls-3.0.2.tgz#9cf24a477ae22bcef5cd5f6f0bfbc1d2d3be9143" - integrity sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ== - dependencies: - abab "^2.0.6" - whatwg-mimetype "^3.0.0" - whatwg-url "^11.0.0" - -date-fns@^2.29.3: - version "2.29.3" - resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.29.3.tgz#27402d2fc67eb442b511b70bbdf98e6411cd68a8" - integrity sha512-dDCnyH2WnnKusqvZZ6+jA1O51Ibt8ZMRNkDZdyAyK4YfbDwa/cEmuztzG5pk6hqlp9aSBPYcjOlktquahGwGeA== - -debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.2, debug@^4.3.4: - version "4.3.4" - resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" - integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== - dependencies: - ms "2.1.2" - -debug@^2.2.0, debug@^2.6.9: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -debug@^3.2.7: - version "3.2.7" - resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" - integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== - dependencies: - ms "^2.1.1" - -decimal.js@^10.3.1: - version "10.3.1" - resolved "https://registry.yarnpkg.com/decimal.js/-/decimal.js-10.3.1.tgz#d8c3a444a9c6774ba60ca6ad7261c3a94fd5e783" - integrity sha512-V0pfhfr8suzyPGOx3nmq4aHqabehUZn6Ch9kyFpV79TGDTWFmHqUqXdabR7QHqxzrYolF4+tVmJhUG4OURg5dQ== - -decode-named-character-reference@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/decode-named-character-reference/-/decode-named-character-reference-1.0.2.tgz#daabac9690874c394c81e4162a0304b35d824f0e" - integrity sha512-O8x12RzrUF8xyVcY0KJowWsmaJxQbmy0/EtnNtHRpsOcT7dFk5W598coHqBVpmWo1oQQfsCqfCmkZN5DJrZVdg== - dependencies: - character-entities "^2.0.0" - -dedent@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" - integrity sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA== - -deep-is@^0.1.3, deep-is@~0.1.3: - version "0.1.4" - resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" - integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== - -deepmerge@^4.2.2: - version "4.2.2" - resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" - integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== - -define-properties@^1.1.3, define-properties@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.4.tgz#0b14d7bd7fbeb2f3572c3a7eda80ea5d57fb05b1" - integrity sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA== - dependencies: - has-property-descriptors "^1.0.0" - object-keys "^1.1.1" - -defined@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/defined/-/defined-1.0.0.tgz#c98d9bcef75674188e110969151199e39b1fa693" - integrity sha512-Y2caI5+ZwS5c3RiNDJ6u53VhQHv+hHKwhkI1iHvceKUHw9Df6EK2zRLfjejRgMuCuxK7PfSWIMwWecceVvThjQ== - -delayed-stream@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" - integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== - -dequal@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be" - integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA== - -detect-newline@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" - integrity sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA== - -detective@^5.2.1: - version "5.2.1" - resolved "https://registry.yarnpkg.com/detective/-/detective-5.2.1.tgz#6af01eeda11015acb0e73f933242b70f24f91034" - integrity sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw== - dependencies: - acorn-node "^1.8.2" - defined "^1.0.0" - minimist "^1.2.6" - -didyoumean@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" - integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw== - -diff-sequences@^28.1.1: - version "28.1.1" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-28.1.1.tgz#9989dc731266dc2903457a70e996f3a041913ac6" - integrity sha512-FU0iFaH/E23a+a718l8Qa/19bF9p06kgE0KipMOMadwa3SjnaElKzPaUC0vnibs6/B/9ni97s61mcejk8W1fQw== - -diff-sequences@^29.0.0: - version "29.0.0" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.0.0.tgz#bae49972ef3933556bcb0800b72e8579d19d9e4f" - integrity sha512-7Qe/zd1wxSDL4D/X/FPjOMB+ZMDt71W94KYaq05I2l0oQqgXgs7s4ftYYmV38gBSrPz2vcygxfs1xn0FT+rKNA== - -diff-sequences@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-29.2.0.tgz#4c55b5b40706c7b5d2c5c75999a50c56d214e8f6" - integrity sha512-413SY5JpYeSBZxmenGEmCVQ8mCgtFJF0w9PROdaS6z987XC2Pd2GOKqOITLtMftmyFZqgtCOb/QA7/Z3ZXfzIw== - -diff@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-5.1.0.tgz#bc52d298c5ea8df9194800224445ed43ffc87e40" - integrity sha512-D+mk+qE8VC/PAUrlAU34N+VfXev0ghe5ywmpqrawphmVZc1bEfn56uo9qpyGp1p4xpzOHkSW4ztBd6L7Xx4ACw== - -dir-glob@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" - integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== - dependencies: - path-type "^4.0.0" - -dlv@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/dlv/-/dlv-1.1.3.tgz#5c198a8a11453596e751494d49874bc7732f2e79" - integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA== - -doctrine@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" - integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw== - dependencies: - esutils "^2.0.2" - -doctrine@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961" - integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w== - dependencies: - esutils "^2.0.2" - -dom-accessibility-api@^0.5.6, dom-accessibility-api@^0.5.9: - version "0.5.14" - resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz#56082f71b1dc7aac69d83c4285eef39c15d93f56" - integrity sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg== - -domexception@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/domexception/-/domexception-4.0.0.tgz#4ad1be56ccadc86fc76d033353999a8037d03673" - integrity sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw== - dependencies: - webidl-conversions "^7.0.0" - -electron-to-chromium@^1.4.202: - version "1.4.218" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.218.tgz#d6b817b5454499a92c85888b42dc2ad075e4493a" - integrity sha512-INDylKH//YIf2w67D+IjkfVnGVrZ/D94DAU/FPPm6T4jEPbEDQvo9r2wTj0ncFdtJH8+V8BggZTaN8Rzk5wkgw== - -electron-to-chromium@^1.4.251: - version "1.4.261" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.261.tgz#31f14ad60c6f95bec404a77a2fd5e1962248e112" - integrity sha512-fVXliNUGJ7XUVJSAasPseBbVgJIeyw5M1xIkgXdTSRjlmCqBbiSTsEdLOCJS31Fc8B7CaloQ/BFAg8By3ODLdg== - -emittery@^0.13.1: - version "0.13.1" - resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.13.1.tgz#c04b8c3457490e0847ae51fced3af52d338e3dad" - integrity sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ== - -emoji-regex@^10.2.1: - version "10.2.1" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.2.1.tgz#a41c330d957191efd3d9dfe6e1e8e1e9ab048b3f" - integrity sha512-97g6QgOk8zlDRdgq1WxwgTMgEWGVAQvB5Fdpgc1MkNy56la5SKP9GsMXKDOdqwn90/41a8yPwIGk1Y6WVbeMQA== - -emoji-regex@^8.0.0: - version "8.0.0" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" - integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== - -emoji-regex@^9.2.2: - version "9.2.2" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" - integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== - -enhanced-resolve@^5.10.0: - version "5.10.0" - resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.10.0.tgz#0dc579c3bb2a1032e357ac45b8f3a6f3ad4fb1e6" - integrity sha512-T0yTFjdpldGY8PmuXXR0PyQ1ufZpEGiHVrp7zHKB7jdR4qlmZHhONVM5AQOAWXuF/w3dnHbEQVrNptJgt7F+cQ== - dependencies: - graceful-fs "^4.2.4" - tapable "^2.2.0" - -entities@^4.3.0: - version "4.3.1" - resolved "https://registry.yarnpkg.com/entities/-/entities-4.3.1.tgz#c34062a94c865c322f9d67b4384e4169bcede6a4" - integrity sha512-o4q/dYJlmyjP2zfnaWDUC6A3BQFmVTX+tZPezK7k0GLSU9QYCauscf5Y+qcEPzKL+EixVouYDgLQK5H9GrLpkg== - -error-ex@^1.3.1: - version "1.3.2" - resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" - integrity sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g== - dependencies: - is-arrayish "^0.2.1" - -error-stack-parser@^2.0.6: - version "2.1.4" - resolved "https://registry.yarnpkg.com/error-stack-parser/-/error-stack-parser-2.1.4.tgz#229cb01cdbfa84440bfa91876285b94680188286" - integrity sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ== - dependencies: - stackframe "^1.3.4" - -es-abstract@^1.19.0, es-abstract@^1.19.1, es-abstract@^1.19.2, es-abstract@^1.19.5: - version "1.20.1" - resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.20.1.tgz#027292cd6ef44bd12b1913b828116f54787d1814" - integrity sha512-WEm2oBhfoI2sImeM4OF2zE2V3BYdSF+KnSi9Sidz51fQHd7+JuF8Xgcj9/0o+OWeIeIS/MiuNnlruQrJf16GQA== - dependencies: - call-bind "^1.0.2" - es-to-primitive "^1.2.1" - function-bind "^1.1.1" - function.prototype.name "^1.1.5" - get-intrinsic "^1.1.1" - get-symbol-description "^1.0.0" - has "^1.0.3" - has-property-descriptors "^1.0.0" - has-symbols "^1.0.3" - internal-slot "^1.0.3" - is-callable "^1.2.4" - is-negative-zero "^2.0.2" - is-regex "^1.1.4" - is-shared-array-buffer "^1.0.2" - is-string "^1.0.7" - is-weakref "^1.0.2" - object-inspect "^1.12.0" - object-keys "^1.1.1" - object.assign "^4.1.2" - regexp.prototype.flags "^1.4.3" - string.prototype.trimend "^1.0.5" - string.prototype.trimstart "^1.0.5" - unbox-primitive "^1.0.2" - -es-shim-unscopables@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" - integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w== - dependencies: - has "^1.0.3" - -es-to-primitive@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a" - integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA== - dependencies: - is-callable "^1.1.4" - is-date-object "^1.0.1" - is-symbol "^1.0.2" - -es5-ext@^0.10.35, es5-ext@^0.10.50: - version "0.10.62" - resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.62.tgz#5e6adc19a6da524bf3d1e02bbc8960e5eb49a9a5" - integrity sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA== - dependencies: - es6-iterator "^2.0.3" - es6-symbol "^3.1.3" - next-tick "^1.1.0" - -es6-iterator@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" - integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g== - dependencies: - d "1" - es5-ext "^0.10.35" - es6-symbol "^3.1.1" - -es6-symbol@^3.1.1, es6-symbol@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" - integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== - dependencies: - d "^1.0.1" - ext "^1.1.2" - -escalade@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" - integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== - -escape-string-regexp@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" - integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== - -escape-string-regexp@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz#a30304e99daa32e23b2fd20f51babd07cffca344" - integrity sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w== - -escape-string-regexp@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" - integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== - -escodegen@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-2.0.0.tgz#5e32b12833e8aa8fa35e1bf0befa89380484c7dd" - integrity sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw== - dependencies: - esprima "^4.0.1" - estraverse "^5.2.0" - esutils "^2.0.2" - optionator "^0.8.1" - optionalDependencies: - source-map "~0.6.1" - -eslint-config-next@13.0.2: - version "13.0.2" - resolved "https://registry.yarnpkg.com/eslint-config-next/-/eslint-config-next-13.0.2.tgz#7c87837821ea7468e018ca41f3bf6fa37d53db68" - integrity sha512-SrrHp+zBDYLjOFZdM5b9aW/pliK687Xxfa+qpDuL08Z04ReHhmz3L+maXaAqgrEVZHQximP7nh0El4yNDJW+CA== - dependencies: - "@next/eslint-plugin-next" "13.0.2" - "@rushstack/eslint-patch" "^1.1.3" - "@typescript-eslint/parser" "^5.21.0" - eslint-import-resolver-node "^0.3.6" - eslint-import-resolver-typescript "^2.7.1" - eslint-plugin-import "^2.26.0" - eslint-plugin-jsx-a11y "^6.5.1" - eslint-plugin-react "^7.31.7" - eslint-plugin-react-hooks "^4.5.0" - -eslint-config-prettier@^8.5.0: - version "8.5.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz#5a81680ec934beca02c7b1a61cf8ca34b66feab1" - integrity sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q== - -eslint-import-resolver-node@^0.3.6: - version "0.3.6" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd" - integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw== - dependencies: - debug "^3.2.7" - resolve "^1.20.0" - -eslint-import-resolver-typescript@^2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-typescript/-/eslint-import-resolver-typescript-2.7.1.tgz#a90a4a1c80da8d632df25994c4c5fdcdd02b8751" - integrity sha512-00UbgGwV8bSgUv34igBDbTOtKhqoRMy9bFjNehT40bXg6585PNIct8HhXZ0SybqB9rWtXj9crcku8ndDn/gIqQ== - dependencies: - debug "^4.3.4" - glob "^7.2.0" - is-glob "^4.0.3" - resolve "^1.22.0" - tsconfig-paths "^3.14.1" - -eslint-module-utils@^2.7.3: - version "2.7.4" - resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz#4f3e41116aaf13a20792261e61d3a2e7e0583974" - integrity sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA== - dependencies: - debug "^3.2.7" - -eslint-plugin-import@^2.26.0: - version "2.26.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b" - integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA== - dependencies: - array-includes "^3.1.4" - array.prototype.flat "^1.2.5" - debug "^2.6.9" - doctrine "^2.1.0" - eslint-import-resolver-node "^0.3.6" - eslint-module-utils "^2.7.3" - has "^1.0.3" - is-core-module "^2.8.1" - is-glob "^4.0.3" - minimatch "^3.1.2" - object.values "^1.1.5" - resolve "^1.22.0" - tsconfig-paths "^3.14.1" - -eslint-plugin-jsx-a11y@^6.5.1: - version "6.6.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.6.1.tgz#93736fc91b83fdc38cc8d115deedfc3091aef1ff" - integrity sha512-sXgFVNHiWffBq23uiS/JaP6eVR622DqwB4yTzKvGZGcPq6/yZ3WmOZfuBks/vHWo9GaFOqC2ZK4i6+C35knx7Q== - dependencies: - "@babel/runtime" "^7.18.9" - aria-query "^4.2.2" - array-includes "^3.1.5" - ast-types-flow "^0.0.7" - axe-core "^4.4.3" - axobject-query "^2.2.0" - damerau-levenshtein "^1.0.8" - emoji-regex "^9.2.2" - has "^1.0.3" - jsx-ast-utils "^3.3.2" - language-tags "^1.0.5" - minimatch "^3.1.2" - semver "^6.3.0" - -eslint-plugin-prettier@^4.2.1: - version "4.2.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz#651cbb88b1dab98bfd42f017a12fa6b2d993f94b" - integrity sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ== - dependencies: - prettier-linter-helpers "^1.0.0" - -eslint-plugin-react-hooks@^4.5.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz#4c3e697ad95b77e93f8646aaa1630c1ba607edd3" - integrity sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g== - -eslint-plugin-react@^7.31.7: - version "7.31.8" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.31.8.tgz#3a4f80c10be1bcbc8197be9e8b641b2a3ef219bf" - integrity sha512-5lBTZmgQmARLLSYiwI71tiGVTLUuqXantZM6vlSY39OaDSV0M7+32K5DnLkmFrwTe+Ksz0ffuLUC91RUviVZfw== - dependencies: - array-includes "^3.1.5" - array.prototype.flatmap "^1.3.0" - doctrine "^2.1.0" - estraverse "^5.3.0" - jsx-ast-utils "^2.4.1 || ^3.0.0" - minimatch "^3.1.2" - object.entries "^1.1.5" - object.fromentries "^2.0.5" - object.hasown "^1.1.1" - object.values "^1.1.5" - prop-types "^15.8.1" - resolve "^2.0.0-next.3" - semver "^6.3.0" - string.prototype.matchall "^4.0.7" - -eslint-scope@^7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642" - integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw== - dependencies: - esrecurse "^4.3.0" - estraverse "^5.2.0" - -eslint-utils@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672" - integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA== - dependencies: - eslint-visitor-keys "^2.0.0" - -eslint-visitor-keys@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" - integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== - -eslint-visitor-keys@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826" - integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA== - -eslint@8.27.0: - version "8.27.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.27.0.tgz#d547e2f7239994ad1faa4bb5d84e5d809db7cf64" - integrity sha512-0y1bfG2ho7mty+SiILVf9PfuRA49ek4Nc60Wmmu62QlobNR+CeXa4xXIJgcuwSQgZiWaPH+5BDsctpIW0PR/wQ== - dependencies: - "@eslint/eslintrc" "^1.3.3" - "@humanwhocodes/config-array" "^0.11.6" - "@humanwhocodes/module-importer" "^1.0.1" - "@nodelib/fs.walk" "^1.2.8" - ajv "^6.10.0" - chalk "^4.0.0" - cross-spawn "^7.0.2" - debug "^4.3.2" - doctrine "^3.0.0" - escape-string-regexp "^4.0.0" - eslint-scope "^7.1.1" - eslint-utils "^3.0.0" - eslint-visitor-keys "^3.3.0" - espree "^9.4.0" - esquery "^1.4.0" - esutils "^2.0.2" - fast-deep-equal "^3.1.3" - file-entry-cache "^6.0.1" - find-up "^5.0.0" - glob-parent "^6.0.2" - globals "^13.15.0" - grapheme-splitter "^1.0.4" - ignore "^5.2.0" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - is-glob "^4.0.0" - is-path-inside "^3.0.3" - js-sdsl "^4.1.4" - js-yaml "^4.1.0" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.4.1" - lodash.merge "^4.6.2" - minimatch "^3.1.2" - natural-compare "^1.4.0" - optionator "^0.9.1" - regexpp "^3.2.0" - strip-ansi "^6.0.1" - strip-json-comments "^3.1.0" - text-table "^0.2.0" - -espree@^9.4.0: - version "9.4.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-9.4.0.tgz#cd4bc3d6e9336c433265fc0aa016fc1aaf182f8a" - integrity sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw== - dependencies: - acorn "^8.8.0" - acorn-jsx "^5.3.2" - eslint-visitor-keys "^3.3.0" - -esprima@^4.0.0, esprima@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/esprima/-/esprima-4.0.1.tgz#13b04cdb3e6c5d19df91ab6987a8695619b0aa71" - integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== - -esquery@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5" - integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w== - dependencies: - estraverse "^5.1.0" - -esrecurse@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" - integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== - dependencies: - estraverse "^5.2.0" - -estraverse@^5.1.0, estraverse@^5.2.0, estraverse@^5.3.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" - integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== - -estree-util-attach-comments@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/estree-util-attach-comments/-/estree-util-attach-comments-2.1.0.tgz#47d69900588bcbc6bf58c3798803ec5f1f3008de" - integrity sha512-rJz6I4L0GaXYtHpoMScgDIwM0/Vwbu5shbMeER596rB2D1EWF6+Gj0e0UKzJPZrpoOc87+Q2kgVFHfjAymIqmw== - dependencies: - "@types/estree" "^1.0.0" - -estree-util-build-jsx@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/estree-util-build-jsx/-/estree-util-build-jsx-2.2.0.tgz#d4307bbeee28c14eb4d63b75c9aad28fa61d84f5" - integrity sha512-apsfRxF9uLrqosApvHVtYZjISPvTJ+lBiIydpC+9wE6cF6ssbhnjyQLqaIjgzGxvC2Hbmec1M7g91PoBayYoQQ== - dependencies: - "@types/estree-jsx" "^1.0.0" - estree-util-is-identifier-name "^2.0.0" - estree-walker "^3.0.0" - -estree-util-is-identifier-name@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/estree-util-is-identifier-name/-/estree-util-is-identifier-name-2.0.1.tgz#cf07867f42705892718d9d89eb2d85eaa8f0fcb5" - integrity sha512-rxZj1GkQhY4x1j/CSnybK9cGuMFQYFPLq0iNyopqf14aOVLFtMv7Esika+ObJWPWiOHuMOAHz3YkWoLYYRnzWQ== - -estree-util-to-js@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/estree-util-to-js/-/estree-util-to-js-1.1.0.tgz#3bd9bb86354063537cc3d81259be2f0d4c3af39f" - integrity sha512-490lbfCcpLk+ofK6HCgqDfYs4KAfq6QVvDw3+Bm1YoKRgiOjKiKYGAVQE1uwh7zVxBgWhqp4FDtp5SqunpUk1A== - dependencies: - "@types/estree-jsx" "^1.0.0" - astring "^1.8.0" - source-map "^0.7.0" - -estree-util-visit@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/estree-util-visit/-/estree-util-visit-1.2.0.tgz#aa0311a9c2f2aa56e9ae5e8b9d87eac14e4ec8f8" - integrity sha512-wdsoqhWueuJKsh5hqLw3j8lwFqNStm92VcwtAOAny8g/KS/l5Y8RISjR4k5W6skCj3Nirag/WUCMS0Nfy3sgsg== - dependencies: - "@types/estree-jsx" "^1.0.0" - "@types/unist" "^2.0.0" - -estree-walker@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-3.0.1.tgz#c2a9fb4a30232f5039b7c030b37ead691932debd" - integrity sha512-woY0RUD87WzMBUiZLx8NsYr23N5BKsOMZHhu2hoNRVh6NXGfoiT1KOL8G3UHlJAnEDGmfa5ubNA/AacfG+Kb0g== - -esutils@^2.0.2: - version "2.0.3" - resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" - integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== - -execa@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd" - integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg== - dependencies: - cross-spawn "^7.0.3" - get-stream "^6.0.0" - human-signals "^2.1.0" - is-stream "^2.0.0" - merge-stream "^2.0.0" - npm-run-path "^4.0.1" - onetime "^5.1.2" - signal-exit "^3.0.3" - strip-final-newline "^2.0.0" - -exit@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/exit/-/exit-0.1.2.tgz#0632638f8d877cc82107d30a0fff1a17cba1cd0c" - integrity sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ== - -expect@^29.0.0: - version "29.0.3" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.0.3.tgz#6be65ddb945202f143c4e07c083f4f39f3bd326f" - integrity sha512-t8l5DTws3212VbmPL+tBFXhjRHLmctHB0oQbL8eUc6S7NzZtYUhycrFO9mkxA0ZUC6FAWdNi7JchJSkODtcu1Q== - dependencies: - "@jest/expect-utils" "^29.0.3" - jest-get-type "^29.0.0" - jest-matcher-utils "^29.0.3" - jest-message-util "^29.0.3" - jest-util "^29.0.3" - -expect@^29.3.0: - version "29.3.0" - resolved "https://registry.yarnpkg.com/expect/-/expect-29.3.0.tgz#2dad3a73ac837dd8074ff91d25cf1614c3e91504" - integrity sha512-bms139btnQNZh4uxCPmzbWz46YOjtEpYIZ847OfY9GCeSBEfzedHWH0CkdR20Sy+XBs8/FI2lFJPZiuH0NGv+w== - dependencies: - "@jest/expect-utils" "^29.2.2" - jest-get-type "^29.2.0" - jest-matcher-utils "^29.2.2" - jest-message-util "^29.2.1" - jest-util "^29.2.1" - -ext@^1.1.2: - version "1.6.0" - resolved "https://registry.yarnpkg.com/ext/-/ext-1.6.0.tgz#3871d50641e874cc172e2b53f919842d19db4c52" - integrity sha512-sdBImtzkq2HpkdRLtlLWDa6w4DX22ijZLKx8BMPUuKe1c5lbN6xwQDQCxSfxBQnHZ13ls/FH0MQZx/q/gr6FQg== - dependencies: - type "^2.5.0" - -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - integrity sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug== - dependencies: - is-extendable "^0.1.0" - -extend@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" - integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== - -fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: - version "3.1.3" - resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" - integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== - -fast-diff@^1.1.2: - version "1.2.0" - resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" - integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== - -fast-glob@^3.2.11, fast-glob@^3.2.9: - version "3.2.11" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9" - integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - -fast-glob@^3.2.12: - version "3.2.12" - resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80" - integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w== - dependencies: - "@nodelib/fs.stat" "^2.0.2" - "@nodelib/fs.walk" "^1.2.3" - glob-parent "^5.1.2" - merge2 "^1.3.0" - micromatch "^4.0.4" - -fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0, fast-json-stable-stringify@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" - integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== - -fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" - integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== - -fast-shallow-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz#d4dcaf6472440dcefa6f88b98e3251e27f25628b" - integrity sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw== - -fastest-stable-stringify@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz#3757a6774f6ec8de40c4e86ec28ea02417214c76" - integrity sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q== - -fastparse@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/fastparse/-/fastparse-1.1.2.tgz#91728c5a5942eced8531283c79441ee4122c35a9" - integrity sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ== - -fastq@^1.6.0: - version "1.13.0" - resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c" - integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw== - dependencies: - reusify "^1.0.4" - -fb-watchman@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" - integrity sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg== - dependencies: - bser "2.1.1" - -fflate@^0.7.3: - version "0.7.4" - resolved "https://registry.yarnpkg.com/fflate/-/fflate-0.7.4.tgz#61587e5d958fdabb5a9368a302c25363f4f69f50" - integrity sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw== - -file-entry-cache@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" - integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg== - dependencies: - flat-cache "^3.0.4" - -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== - dependencies: - to-regex-range "^5.0.1" - -find-up@^4.0.0, find-up@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" - integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== - dependencies: - locate-path "^5.0.0" - path-exists "^4.0.0" - -find-up@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" - integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== - dependencies: - locate-path "^6.0.0" - path-exists "^4.0.0" - -flat-cache@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11" - integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg== - dependencies: - flatted "^3.1.0" - rimraf "^3.0.2" - -flatted@^3.1.0: - version "3.2.6" - resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.6.tgz#022e9218c637f9f3fc9c35ab9c9193f05add60b2" - integrity sha512-0sQoMh9s0BYsm+12Huy/rkKxVu4R1+r96YX5cG44rHV0pQ6iC3Q+mkoMFaGWObMFYQxCVT+ssG1ksneA2MI9KQ== - -form-data@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452" - integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww== - dependencies: - asynckit "^0.4.0" - combined-stream "^1.0.8" - mime-types "^2.1.12" - -fraction.js@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950" - integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA== - -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== - -fsevents@^2.3.2, fsevents@~2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" - integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== - -function-bind@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" - integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== - -function.prototype.name@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" - integrity sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.0" - functions-have-names "^1.2.2" - -functions-have-names@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" - integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== - -gensync@^1.0.0-beta.2: - version "1.0.0-beta.2" - resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0" - integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg== - -get-caller-file@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" - integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== - -get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.2.tgz#336975123e05ad0b7ba41f152ee4aadbea6cf598" - integrity sha512-Jfm3OyCxHh9DJyc28qGk+JmfkpO41A4XkneDSujN9MDXrm4oDKdHvndhZ2dN94+ERNfkYJWDclW6k2L/ZGHjXA== - dependencies: - function-bind "^1.1.1" - has "^1.0.3" - has-symbols "^1.0.3" - -get-package-type@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" - integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== - -get-stream@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7" - integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg== - -get-symbol-description@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6" - integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw== - dependencies: - call-bind "^1.0.2" - get-intrinsic "^1.1.1" - -glob-parent@^5.1.2, glob-parent@~5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" - integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== - dependencies: - is-glob "^4.0.1" - -glob-parent@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" - integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== - dependencies: - is-glob "^4.0.3" - -glob@7.1.7: - version "7.1.7" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" - integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.0.4" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^7.1.3, glob@^7.1.4, glob@^7.2.0: - version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== - dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" - -globals@^11.1.0: - version "11.12.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" - integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== - -globals@^13.15.0: - version "13.17.0" - resolved "https://registry.yarnpkg.com/globals/-/globals-13.17.0.tgz#902eb1e680a41da93945adbdcb5a9f361ba69bd4" - integrity sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw== - dependencies: - type-fest "^0.20.2" - -globby@^11.1.0: - version "11.1.0" - resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" - integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== - dependencies: - array-union "^2.1.0" - dir-glob "^3.0.1" - fast-glob "^3.2.9" - ignore "^5.2.0" - merge2 "^1.4.1" - slash "^3.0.0" - -graceful-fs@^4.2.4, graceful-fs@^4.2.9: - version "4.2.10" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" - integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== - -grapheme-splitter@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e" - integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ== - -gray-matter@^4.0.3: - version "4.0.3" - resolved "https://registry.yarnpkg.com/gray-matter/-/gray-matter-4.0.3.tgz#e893c064825de73ea1f5f7d88c7a9f7274288798" - integrity sha512-5v6yZd4JK3eMI3FqqCouswVqwugaA9r4dNZB1wwcmrD02QkV5H0y7XBQW8QwQqEaZY1pM9aqORSORhJRdNK44Q== - dependencies: - js-yaml "^3.13.1" - kind-of "^6.0.2" - section-matter "^1.0.0" - strip-bom-string "^1.0.0" - -has-bigints@^1.0.1, has-bigints@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa" - integrity sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ== - -has-flag@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" - integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw== - -has-flag@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" - integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== - -has-property-descriptors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861" - integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ== - dependencies: - get-intrinsic "^1.1.1" - -has-symbols@^1.0.2, has-symbols@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" - integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== - -has-tostringtag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25" - integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ== - dependencies: - has-symbols "^1.0.2" - -has@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" - integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== - dependencies: - function-bind "^1.1.1" - -hast-util-to-estree@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/hast-util-to-estree/-/hast-util-to-estree-2.1.0.tgz#aeac70aad0102ae309570907b3f56a08231d5323" - integrity sha512-Vwch1etMRmm89xGgz+voWXvVHba2iiMdGMKmaMfYt35rbVtFDq8JNwwAIvi8zHMkO6Gvqo9oTMwJTmzVRfXh4g== - dependencies: - "@types/estree" "^1.0.0" - "@types/estree-jsx" "^1.0.0" - "@types/hast" "^2.0.0" - "@types/unist" "^2.0.0" - comma-separated-tokens "^2.0.0" - estree-util-attach-comments "^2.0.0" - estree-util-is-identifier-name "^2.0.0" - hast-util-whitespace "^2.0.0" - mdast-util-mdx-expression "^1.0.0" - mdast-util-mdxjs-esm "^1.0.0" - property-information "^6.0.0" - space-separated-tokens "^2.0.0" - style-to-object "^0.3.0" - unist-util-position "^4.0.0" - zwitch "^2.0.0" - -hast-util-whitespace@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/hast-util-whitespace/-/hast-util-whitespace-2.0.0.tgz#4fc1086467cc1ef5ba20673cb6b03cec3a970f1c" - integrity sha512-Pkw+xBHuV6xFeJprJe2BBEoDV+AvQySaz3pPDRUs5PNZEMQjpXJJueqrpcHIXxnWTcAGi/UOCgVShlkY6kLoqg== - -html-encoding-sniffer@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9" - integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA== - dependencies: - whatwg-encoding "^2.0.0" - -html-escaper@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" - integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== - -http-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" - integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== - dependencies: - "@tootallnate/once" "2" - agent-base "6" - debug "4" - -https-proxy-agent@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" - integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== - dependencies: - agent-base "6" - debug "4" - -human-signals@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0" - integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw== - -hyphenate-style-name@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz#691879af8e220aea5750e8827db4ef62a54e361d" - integrity sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ== - -iconv-lite@0.6.3: - version "0.6.3" - resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" - integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== - dependencies: - safer-buffer ">= 2.1.2 < 3.0.0" - -ignore@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" - integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== - -import-fresh@^3.0.0, import-fresh@^3.2.1: - version "3.3.0" - resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" - integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== - dependencies: - parent-module "^1.0.0" - resolve-from "^4.0.0" - -import-local@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" - integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== - dependencies: - pkg-dir "^4.2.0" - resolve-cwd "^3.0.0" - -imurmurhash@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" - integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== - -indent-string@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" - integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -inline-style-parser@0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/inline-style-parser/-/inline-style-parser-0.1.1.tgz#ec8a3b429274e9c0a1f1c4ffa9453a7fef72cea1" - integrity sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q== - -inline-style-prefixer@^6.0.0: - version "6.0.1" - resolved "https://registry.yarnpkg.com/inline-style-prefixer/-/inline-style-prefixer-6.0.1.tgz#c5c0e43ba8831707afc5f5bbfd97edf45c1fa7ae" - integrity sha512-AsqazZ8KcRzJ9YPN1wMH2aNM7lkWQ8tSPrW5uDk1ziYwiAPWSZnUsC7lfZq+BDqLqz0B4Pho5wscWcJzVvRzDQ== - dependencies: - css-in-js-utils "^2.0.0" - -internal-slot@^1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c" - integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA== - dependencies: - get-intrinsic "^1.1.0" - has "^1.0.3" - side-channel "^1.0.4" - -is-alphabetical@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-2.0.1.tgz#01072053ea7c1036df3c7d19a6daaec7f19e789b" - integrity sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ== - -is-alphanumerical@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz#7c03fbe96e3e931113e57f964b0a368cc2dfd875" - integrity sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw== - dependencies: - is-alphabetical "^2.0.0" - is-decimal "^2.0.0" - -is-arrayish@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" - integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg== - -is-arrayish@^0.3.1: - version "0.3.2" - resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.2.tgz#4574a2ae56f7ab206896fb431eaeed066fdf8f03" - integrity sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ== - -is-bigint@^1.0.1: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3" - integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg== - dependencies: - has-bigints "^1.0.1" - -is-binary-path@~2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" - integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== - dependencies: - binary-extensions "^2.0.0" - -is-boolean-object@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" - integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-buffer@^2.0.0: - version "2.0.5" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-2.0.5.tgz#ebc252e400d22ff8d77fa09888821a24a658c191" - integrity sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ== - -is-callable@^1.1.4, is-callable@^1.2.4: - version "1.2.4" - resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945" - integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w== - -is-core-module@^2.8.1, is-core-module@^2.9.0: - version "2.10.0" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.10.0.tgz#9012ede0a91c69587e647514e1d5277019e728ed" - integrity sha512-Erxj2n/LDAZ7H8WNJXd9tw38GYM3dv8rk8Zcs+jJuxYTW7sozH+SS8NtrSjVL1/vpLvWi1hxy96IzjJ3EHTJJg== - dependencies: - has "^1.0.3" - -is-date-object@^1.0.1: - version "1.0.5" - resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" - integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ== - dependencies: - has-tostringtag "^1.0.0" - -is-decimal@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-2.0.1.tgz#9469d2dc190d0214fd87d78b78caecc0cc14eef7" - integrity sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A== - -is-extendable@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - integrity sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw== - -is-extglob@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" - integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== - -is-fullwidth-code-point@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" - integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== - -is-generator-fn@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" - integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== - -is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: - version "4.0.3" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" - integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== - dependencies: - is-extglob "^2.1.1" - -is-hexadecimal@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz#86b5bf668fca307498d319dfc03289d781a90027" - integrity sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg== - -is-negative-zero@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" - integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== - -is-number-object@^1.0.4: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc" - integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ== - dependencies: - has-tostringtag "^1.0.0" - -is-number@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" - integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== - -is-path-inside@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/is-path-inside/-/is-path-inside-3.0.3.tgz#d231362e53a07ff2b0e0ea7fed049161ffd16283" - integrity sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ== - -is-plain-obj@^4.0.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz#d65025edec3657ce032fd7db63c97883eaed71f0" - integrity sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg== - -is-potential-custom-element-name@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz#171ed6f19e3ac554394edf78caa05784a45bebb5" - integrity sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ== - -is-reference@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-3.0.0.tgz#b1380c03d96ddf7089709781e3208fceb0c92cd6" - integrity sha512-Eo1W3wUoHWoCoVM4GVl/a+K0IgiqE5aIo4kJABFyMum1ZORlPkC+UC357sSQUL5w5QCE5kCC9upl75b7+7CY/Q== - dependencies: - "@types/estree" "*" - -is-regex@^1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958" - integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg== - dependencies: - call-bind "^1.0.2" - has-tostringtag "^1.0.0" - -is-shared-array-buffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79" - integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA== - dependencies: - call-bind "^1.0.2" - -is-stream@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== - -is-string@^1.0.5, is-string@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd" - integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg== - dependencies: - has-tostringtag "^1.0.0" - -is-symbol@^1.0.2, is-symbol@^1.0.3: - version "1.0.4" - resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c" - integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg== - dependencies: - has-symbols "^1.0.2" - -is-typedarray@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" - integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA== - -is-weakref@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2" - integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ== - dependencies: - call-bind "^1.0.2" - -isexe@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" - integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== - -isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== - -istanbul-lib-coverage@^3.0.0, istanbul-lib-coverage@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz#189e7909d0a39fa5a3dfad5b03f71947770191d3" - integrity sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw== - -istanbul-lib-instrument@^5.0.4, istanbul-lib-instrument@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.0.tgz#31d18bdd127f825dd02ea7bfdfd906f8ab840e9f" - integrity sha512-6Lthe1hqXHBNsqvgDzGO6l03XNeu3CrG4RqQ1KM9+l5+jNGpEJfIELx1NS3SEHmJQA8np/u+E4EPRKRiu6m19A== - dependencies: - "@babel/core" "^7.12.3" - "@babel/parser" "^7.14.7" - "@istanbuljs/schema" "^0.1.2" - istanbul-lib-coverage "^3.2.0" - semver "^6.3.0" - -istanbul-lib-report@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz#7518fe52ea44de372f460a76b5ecda9ffb73d8a6" - integrity sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw== - dependencies: - istanbul-lib-coverage "^3.0.0" - make-dir "^3.0.0" - supports-color "^7.1.0" - -istanbul-lib-source-maps@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz#895f3a709fcfba34c6de5a42939022f3e4358551" - integrity sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw== - dependencies: - debug "^4.1.1" - istanbul-lib-coverage "^3.0.0" - source-map "^0.6.1" - -istanbul-reports@^3.1.3: - version "3.1.5" - resolved "https://registry.yarnpkg.com/istanbul-reports/-/istanbul-reports-3.1.5.tgz#cc9a6ab25cb25659810e4785ed9d9fb742578bae" - integrity sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w== - dependencies: - html-escaper "^2.0.0" - istanbul-lib-report "^3.0.0" - -jest-changed-files@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-29.2.0.tgz#b6598daa9803ea6a4dce7968e20ab380ddbee289" - integrity sha512-qPVmLLyBmvF5HJrY7krDisx6Voi8DmlV3GZYX0aFNbaQsZeoz1hfxcCMbqDGuQCxU1dJy9eYc2xscE8QrCCYaA== - dependencies: - execa "^5.0.0" - p-limit "^3.1.0" - -jest-circus@^29.3.0: - version "29.3.0" - resolved "https://registry.yarnpkg.com/jest-circus/-/jest-circus-29.3.0.tgz#997354276d24706e14549cab98ac2995d63c92fd" - integrity sha512-xL1cmbUGBGy923KBZpZ2LRKspHlIhrltrwGaefJ677HXCPY5rTF758BtweamBype2ogcSEK/oqcp1SmYZ/ATig== - dependencies: - "@jest/environment" "^29.3.0" - "@jest/expect" "^29.3.0" - "@jest/test-result" "^29.2.1" - "@jest/types" "^29.2.1" - "@types/node" "*" - chalk "^4.0.0" - co "^4.6.0" - dedent "^0.7.0" - is-generator-fn "^2.0.0" - jest-each "^29.2.1" - jest-matcher-utils "^29.2.2" - jest-message-util "^29.2.1" - jest-runtime "^29.3.0" - jest-snapshot "^29.3.0" - jest-util "^29.2.1" - p-limit "^3.1.0" - pretty-format "^29.2.1" - slash "^3.0.0" - stack-utils "^2.0.3" - -jest-cli@^29.3.0: - version "29.3.0" - resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-29.3.0.tgz#7ca1913f6088570ae58bbb312d70500e00120d41" - integrity sha512-rDb9iasZvqTkgrlwzVGemR5i20T0/XN1ug46Ch2vxTRa0zS5PHaVXQXYzYbuLFHs1xpc+XsB9xPfEkkwbnLJBg== - dependencies: - "@jest/core" "^29.3.0" - "@jest/test-result" "^29.2.1" - "@jest/types" "^29.2.1" - chalk "^4.0.0" - exit "^0.1.2" - graceful-fs "^4.2.9" - import-local "^3.0.2" - jest-config "^29.3.0" - jest-util "^29.2.1" - jest-validate "^29.2.2" - prompts "^2.0.1" - yargs "^17.3.1" - -jest-config@^29.3.0: - version "29.3.0" - resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-29.3.0.tgz#4b30188390636106414ea6b43aa6b2fb30d1a54d" - integrity sha512-sTSDs/M+//njznsytxiBxwfDnSWRb6OqiNSlO/B2iw1HUaa1YLsdWmV4AWLXss1XKzv1F0yVK+kA4XOhZ0I1qQ== - dependencies: - "@babel/core" "^7.11.6" - "@jest/test-sequencer" "^29.3.0" - "@jest/types" "^29.2.1" - babel-jest "^29.3.0" - chalk "^4.0.0" - ci-info "^3.2.0" - deepmerge "^4.2.2" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-circus "^29.3.0" - jest-environment-node "^29.3.0" - jest-get-type "^29.2.0" - jest-regex-util "^29.2.0" - jest-resolve "^29.3.0" - jest-runner "^29.3.0" - jest-util "^29.2.1" - jest-validate "^29.2.2" - micromatch "^4.0.4" - parse-json "^5.2.0" - pretty-format "^29.2.1" - slash "^3.0.0" - strip-json-comments "^3.1.1" - -jest-diff@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-28.1.3.tgz#948a192d86f4e7a64c5264ad4da4877133d8792f" - integrity sha512-8RqP1B/OXzjjTWkqMX67iqgwBVJRgCyKD3L9nq+6ZqJMdvjE8RgHktqZ6jNrkdMT+dJuYNI3rhQpxaz7drJHfw== - dependencies: - chalk "^4.0.0" - diff-sequences "^28.1.1" - jest-get-type "^28.0.2" - pretty-format "^28.1.3" - -jest-diff@^29.0.3: - version "29.0.3" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.0.3.tgz#41cc02409ad1458ae1bf7684129a3da2856341ac" - integrity sha512-+X/AIF5G/vX9fWK+Db9bi9BQas7M9oBME7egU7psbn4jlszLFCu0dW63UgeE6cs/GANq4fLaT+8sGHQQ0eCUfg== - dependencies: - chalk "^4.0.0" - diff-sequences "^29.0.0" - jest-get-type "^29.0.0" - pretty-format "^29.0.3" - -jest-diff@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-29.2.1.tgz#027e42f5a18b693fb2e88f81b0ccab533c08faee" - integrity sha512-gfh/SMNlQmP3MOUgdzxPOd4XETDJifADpT937fN1iUGz+9DgOu2eUPHH25JDkLVcLwwqxv3GzVyK4VBUr9fjfA== - dependencies: - chalk "^4.0.0" - diff-sequences "^29.2.0" - jest-get-type "^29.2.0" - pretty-format "^29.2.1" - -jest-docblock@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-29.2.0.tgz#307203e20b637d97cee04809efc1d43afc641e82" - integrity sha512-bkxUsxTgWQGbXV5IENmfiIuqZhJcyvF7tU4zJ/7ioTutdz4ToB5Yx6JOFBpgI+TphRY4lhOyCWGNH/QFQh5T6A== - dependencies: - detect-newline "^3.0.0" - -jest-each@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-29.2.1.tgz#6b0a88ee85c2ba27b571a6010c2e0c674f5c9b29" - integrity sha512-sGP86H/CpWHMyK3qGIGFCgP6mt+o5tu9qG4+tobl0LNdgny0aitLXs9/EBacLy3Bwqy+v4uXClqJgASJWcruYw== - dependencies: - "@jest/types" "^29.2.1" - chalk "^4.0.0" - jest-get-type "^29.2.0" - jest-util "^29.2.1" - pretty-format "^29.2.1" - -jest-environment-jsdom@29.3.0: - version "29.3.0" - resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-29.3.0.tgz#378d750e3c6894efa543ba8f7261c0a3d892d42e" - integrity sha512-xFLbMR4OF4lntNcO9LthJdPRbI9WgfFlG73aQS6wQ54+v4oSAp8T4FKUw0add+Z+Ghu/dirRxuvc4FzzN5kRxw== - dependencies: - "@jest/environment" "^29.3.0" - "@jest/fake-timers" "^29.3.0" - "@jest/types" "^29.2.1" - "@types/jsdom" "^20.0.0" - "@types/node" "*" - jest-mock "^29.3.0" - jest-util "^29.2.1" - jsdom "^20.0.0" - -jest-environment-node@^29.3.0: - version "29.3.0" - resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-29.3.0.tgz#aed95a8e566d80f9f8564fba01ca5caa9d0aa59a" - integrity sha512-oikVE5pyiBUMrqi7J/kFGd1zeT14+EnJulyqzopDNijLX13ygwjiOF/GVpVKSGyBrrAwSkaj/ohEQJCcjkCtOA== - dependencies: - "@jest/environment" "^29.3.0" - "@jest/fake-timers" "^29.3.0" - "@jest/types" "^29.2.1" - "@types/node" "*" - jest-mock "^29.3.0" - jest-util "^29.2.1" - -jest-fetch-mock@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/jest-fetch-mock/-/jest-fetch-mock-3.0.3.tgz#31749c456ae27b8919d69824f1c2bd85fe0a1f3b" - integrity sha512-Ux1nWprtLrdrH4XwE7O7InRY6psIi3GOsqNESJgMJ+M5cv4A8Lh7SN9d2V2kKRZ8ebAfcd1LNyZguAOb6JiDqw== - dependencies: - cross-fetch "^3.0.4" - promise-polyfill "^8.1.3" - -jest-get-type@^28.0.2: - version "28.0.2" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-28.0.2.tgz#34622e628e4fdcd793d46db8a242227901fcf203" - integrity sha512-ioj2w9/DxSYHfOm5lJKCdcAmPJzQXmbM/Url3rhlghrPvT3tt+7a/+oXc9azkKmLvoiXjtV83bEWqi+vs5nlPA== - -jest-get-type@^29.0.0: - version "29.0.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.0.0.tgz#843f6c50a1b778f7325df1129a0fd7aa713aef80" - integrity sha512-83X19z/HuLKYXYHskZlBAShO7UfLFXu/vWajw9ZNJASN32li8yHMaVGAQqxFW1RCFOkB7cubaL6FaJVQqqJLSw== - -jest-get-type@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-29.2.0.tgz#726646f927ef61d583a3b3adb1ab13f3a5036408" - integrity sha512-uXNJlg8hKFEnDgFsrCjznB+sTxdkuqiCL6zMgA75qEbAJjJYTs9XPrvDctrEig2GDow22T/LvHgO57iJhXB/UA== - -jest-haste-map@^29.3.0: - version "29.3.0" - resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-29.3.0.tgz#9786f501ed6ab1c7adc35d3f83c1c21ed4172c77" - integrity sha512-ugdLIreycMRRg3+6AjiExECmuFI2D9PS+BmNU7eGvBt3fzVMKybb9USAZXN6kw4Q6Mn8DSK+7OFCloY2rN820Q== - dependencies: - "@jest/types" "^29.2.1" - "@types/graceful-fs" "^4.1.3" - "@types/node" "*" - anymatch "^3.0.3" - fb-watchman "^2.0.0" - graceful-fs "^4.2.9" - jest-regex-util "^29.2.0" - jest-util "^29.2.1" - jest-worker "^29.3.0" - micromatch "^4.0.4" - walker "^1.0.8" - optionalDependencies: - fsevents "^2.3.2" - -jest-leak-detector@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-29.2.1.tgz#ec551686b7d512ec875616c2c3534298b1ffe2fc" - integrity sha512-1YvSqYoiurxKOJtySc+CGVmw/e1v4yNY27BjWTVzp0aTduQeA7pdieLiW05wTYG/twlKOp2xS/pWuikQEmklug== - dependencies: - jest-get-type "^29.2.0" - pretty-format "^29.2.1" - -jest-matcher-utils@^28.0.0: - version "28.1.3" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-28.1.3.tgz#5a77f1c129dd5ba3b4d7fc20728806c78893146e" - integrity sha512-kQeJ7qHemKfbzKoGjHHrRKH6atgxMk8Enkk2iPQ3XwO6oE/KYD8lMYOziCkeSB9G4adPM4nR1DE8Tf5JeWH6Bw== - dependencies: - chalk "^4.0.0" - jest-diff "^28.1.3" - jest-get-type "^28.0.2" - pretty-format "^28.1.3" - -jest-matcher-utils@^29.0.3: - version "29.0.3" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.0.3.tgz#b8305fd3f9e27cdbc210b21fc7dbba92d4e54560" - integrity sha512-RsR1+cZ6p1hDV4GSCQTg+9qjeotQCgkaleIKLK7dm+U4V/H2bWedU3RAtLm8+mANzZ7eDV33dMar4pejd7047w== - dependencies: - chalk "^4.0.0" - jest-diff "^29.0.3" - jest-get-type "^29.0.0" - pretty-format "^29.0.3" - -jest-matcher-utils@^29.2.2: - version "29.2.2" - resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-29.2.2.tgz#9202f8e8d3a54733266784ce7763e9a08688269c" - integrity sha512-4DkJ1sDPT+UX2MR7Y3od6KtvRi9Im1ZGLGgdLFLm4lPexbTaCgJW5NN3IOXlQHF7NSHY/VHhflQ+WoKtD/vyCw== - dependencies: - chalk "^4.0.0" - jest-diff "^29.2.1" - jest-get-type "^29.2.0" - pretty-format "^29.2.1" - -jest-message-util@^29.0.3: - version "29.0.3" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.0.3.tgz#f0254e1ffad21890c78355726202cc91d0a40ea8" - integrity sha512-7T8JiUTtDfppojosORAflABfLsLKMLkBHSWkjNQrjIltGoDzNGn7wEPOSfjqYAGTYME65esQzMJxGDjuLBKdOg== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.0.3" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - micromatch "^4.0.4" - pretty-format "^29.0.3" - slash "^3.0.0" - stack-utils "^2.0.3" - -jest-message-util@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-29.2.1.tgz#3a51357fbbe0cc34236f17a90d772746cf8d9193" - integrity sha512-Dx5nEjw9V8C1/Yj10S/8ivA8F439VS8vTq1L7hEgwHFn9ovSKNpYW/kwNh7UglaEgXO42XxzKJB+2x0nSglFVw== - dependencies: - "@babel/code-frame" "^7.12.13" - "@jest/types" "^29.2.1" - "@types/stack-utils" "^2.0.0" - chalk "^4.0.0" - graceful-fs "^4.2.9" - micromatch "^4.0.4" - pretty-format "^29.2.1" - slash "^3.0.0" - stack-utils "^2.0.3" - -jest-mock@^29.3.0: - version "29.3.0" - resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-29.3.0.tgz#aa6f1d5118fd4b9d007df782e0624e6efab6d33d" - integrity sha512-BRKfsAaeP3pTWeog+1D0ILeJF96SzB6y3k0JDxY63kssxiUy9nDLHmNUoVkBGILjMbpHULhbzVTsb3harPXuUQ== - dependencies: - "@jest/types" "^29.2.1" - "@types/node" "*" - jest-util "^29.2.1" - -jest-pnp-resolver@^1.2.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz#b704ac0ae028a89108a4d040b3f919dfddc8e33c" - integrity sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w== - -jest-regex-util@^29.2.0: - version "29.2.0" - resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-29.2.0.tgz#82ef3b587e8c303357728d0322d48bbfd2971f7b" - integrity sha512-6yXn0kg2JXzH30cr2NlThF+70iuO/3irbaB4mh5WyqNIvLLP+B6sFdluO1/1RJmslyh/f9osnefECflHvTbwVA== - -jest-resolve-dependencies@^29.3.0: - version "29.3.0" - resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-29.3.0.tgz#9a2c3389d1a4cce95445f7c34b0b25f44fff9fad" - integrity sha512-ykSbDbWmIaHprOBig57AExw7i6Fj0y69M6baiAd75Ivx1UMQt4wsM6A+SNqIhycV6Zy8XV3L40Ac3HYSrDSq7w== - dependencies: - jest-regex-util "^29.2.0" - jest-snapshot "^29.3.0" - -jest-resolve@^29.3.0: - version "29.3.0" - resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-29.3.0.tgz#038711e9af86f4b4f55a407db14310bc57a8beb4" - integrity sha512-xH6C6loDlOWEWHdCgioLDlbpmsolNdNsV/UR35ChuK217x0ttHuhyEPdh5wa6CTQ/Eq4OGW2/EZTlh0ay5aojQ== - dependencies: - chalk "^4.0.0" - graceful-fs "^4.2.9" - jest-haste-map "^29.3.0" - jest-pnp-resolver "^1.2.2" - jest-util "^29.2.1" - jest-validate "^29.2.2" - resolve "^1.20.0" - resolve.exports "^1.1.0" - slash "^3.0.0" - -jest-runner@^29.3.0: - version "29.3.0" - resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-29.3.0.tgz#fc255482594c1536a34e2b41f610355c4f3d2ee6" - integrity sha512-E/ROzAVj7gy44FvIe+Tbz0xGWG1sa8WLkhUg/hsXHewPC0Z48kqWySdfYRtXkB7RmMn4OcWE+hIBfsRAMVV+sQ== - dependencies: - "@jest/console" "^29.2.1" - "@jest/environment" "^29.3.0" - "@jest/test-result" "^29.2.1" - "@jest/transform" "^29.3.0" - "@jest/types" "^29.2.1" - "@types/node" "*" - chalk "^4.0.0" - emittery "^0.13.1" - graceful-fs "^4.2.9" - jest-docblock "^29.2.0" - jest-environment-node "^29.3.0" - jest-haste-map "^29.3.0" - jest-leak-detector "^29.2.1" - jest-message-util "^29.2.1" - jest-resolve "^29.3.0" - jest-runtime "^29.3.0" - jest-util "^29.2.1" - jest-watcher "^29.2.2" - jest-worker "^29.3.0" - p-limit "^3.1.0" - source-map-support "0.5.13" - -jest-runtime@^29.3.0: - version "29.3.0" - resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-29.3.0.tgz#e44f838f469ab1f6b524178bae6233697748eb27" - integrity sha512-ufgX/hbpa7MLnjWRW82T5mVF73FBk3W38dGCLPXWtYZ5Zr1ZFh8QnaAtITKJt0p3kGXR8ZqlIjadSiBTk/QJ/A== - dependencies: - "@jest/environment" "^29.3.0" - "@jest/fake-timers" "^29.3.0" - "@jest/globals" "^29.3.0" - "@jest/source-map" "^29.2.0" - "@jest/test-result" "^29.2.1" - "@jest/transform" "^29.3.0" - "@jest/types" "^29.2.1" - "@types/node" "*" - chalk "^4.0.0" - cjs-module-lexer "^1.0.0" - collect-v8-coverage "^1.0.0" - glob "^7.1.3" - graceful-fs "^4.2.9" - jest-haste-map "^29.3.0" - jest-message-util "^29.2.1" - jest-mock "^29.3.0" - jest-regex-util "^29.2.0" - jest-resolve "^29.3.0" - jest-snapshot "^29.3.0" - jest-util "^29.2.1" - slash "^3.0.0" - strip-bom "^4.0.0" - -jest-snapshot@^29.3.0: - version "29.3.0" - resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-29.3.0.tgz#e319cb98cf36640194717fb37a9bd048a3e448f8" - integrity sha512-+4mX3T8XI3ABbZFzBd/AM74mfwOb6gMpYVFNTc0Cgg2F2fGYvHii8D6jWWka99a3wyNFmni3ov8meEVTF8n13Q== - dependencies: - "@babel/core" "^7.11.6" - "@babel/generator" "^7.7.2" - "@babel/plugin-syntax-jsx" "^7.7.2" - "@babel/plugin-syntax-typescript" "^7.7.2" - "@babel/traverse" "^7.7.2" - "@babel/types" "^7.3.3" - "@jest/expect-utils" "^29.2.2" - "@jest/transform" "^29.3.0" - "@jest/types" "^29.2.1" - "@types/babel__traverse" "^7.0.6" - "@types/prettier" "^2.1.5" - babel-preset-current-node-syntax "^1.0.0" - chalk "^4.0.0" - expect "^29.3.0" - graceful-fs "^4.2.9" - jest-diff "^29.2.1" - jest-get-type "^29.2.0" - jest-haste-map "^29.3.0" - jest-matcher-utils "^29.2.2" - jest-message-util "^29.2.1" - jest-util "^29.2.1" - natural-compare "^1.4.0" - pretty-format "^29.2.1" - semver "^7.3.5" - -jest-util@^29.0.0, jest-util@^29.0.3: - version "29.0.3" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.0.3.tgz#06d1d77f9a1bea380f121897d78695902959fbc0" - integrity sha512-Q0xaG3YRG8QiTC4R6fHjHQPaPpz9pJBEi0AeOE4mQh/FuWOijFjGXMMOfQEaU9i3z76cNR7FobZZUQnL6IyfdQ== - dependencies: - "@jest/types" "^29.0.3" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - -jest-util@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-29.2.1.tgz#f26872ba0dc8cbefaba32c34f98935f6cf5fc747" - integrity sha512-P5VWDj25r7kj7kl4pN2rG/RN2c1TLfYYYZYULnS/35nFDjBai+hBeo3MDrYZS7p6IoY3YHZnt2vq4L6mKnLk0g== - dependencies: - "@jest/types" "^29.2.1" - "@types/node" "*" - chalk "^4.0.0" - ci-info "^3.2.0" - graceful-fs "^4.2.9" - picomatch "^2.2.3" - -jest-validate@^29.2.2: - version "29.2.2" - resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-29.2.2.tgz#e43ce1931292dfc052562a11bc681af3805eadce" - integrity sha512-eJXATaKaSnOuxNfs8CLHgdABFgUrd0TtWS8QckiJ4L/QVDF4KVbZFBBOwCBZHOS0Rc5fOxqngXeGXE3nGQkpQA== - dependencies: - "@jest/types" "^29.2.1" - camelcase "^6.2.0" - chalk "^4.0.0" - jest-get-type "^29.2.0" - leven "^3.1.0" - pretty-format "^29.2.1" - -jest-watcher@^29.2.2: - version "29.2.2" - resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-29.2.2.tgz#7093d4ea8177e0a0da87681a9e7b09a258b9daf7" - integrity sha512-j2otfqh7mOvMgN2WlJ0n7gIx9XCMWntheYGlBK7+5g3b1Su13/UAK7pdKGyd4kDlrLwtH2QPvRv5oNIxWvsJ1w== - dependencies: - "@jest/test-result" "^29.2.1" - "@jest/types" "^29.2.1" - "@types/node" "*" - ansi-escapes "^4.2.1" - chalk "^4.0.0" - emittery "^0.13.1" - jest-util "^29.2.1" - string-length "^4.0.1" - -jest-worker@^29.3.0: - version "29.3.0" - resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-29.3.0.tgz#240a1cd731c7d6645e8bcc37a3d584f122afb44a" - integrity sha512-rP8LYClB5NCWW0p8GdQT9vRmZNrDmjypklEYZuGCIU5iNviVWCZK5MILS3rQwD0FY1u96bY7b+KoU17DdZy6Ww== - dependencies: - "@types/node" "*" - jest-util "^29.2.1" - merge-stream "^2.0.0" - supports-color "^8.0.0" - -jest@29.3.0: - version "29.3.0" - resolved "https://registry.yarnpkg.com/jest/-/jest-29.3.0.tgz#edb9ef5b308e90e1c717c8accc04b28f133ac66d" - integrity sha512-lWmHtOcJSjR6FYRw+4oo7456QUe6LN73Lw6HLwOWKTPLcyQF60cMh0EoIHi67dV74SY5tw/kL+jYC+Ji43ScUg== - dependencies: - "@jest/core" "^29.3.0" - "@jest/types" "^29.2.1" - import-local "^3.0.2" - jest-cli "^29.3.0" - -js-cookie@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" - integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ== - -js-sdsl@^4.1.4: - version "4.1.5" - resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.1.5.tgz#1ff1645e6b4d1b028cd3f862db88c9d887f26e2a" - integrity sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q== - -"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" - integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== - -js-yaml@^3.13.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.14.1.tgz#dae812fdb3825fa306609a8717383c50c36a0537" - integrity sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g== - dependencies: - argparse "^1.0.7" - esprima "^4.0.0" - -js-yaml@^4.0.0, js-yaml@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" - integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== - dependencies: - argparse "^2.0.1" - -jsdom@^20.0.0: - version "20.0.0" - resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-20.0.0.tgz#882825ac9cc5e5bbee704ba16143e1fa78361ebf" - integrity sha512-x4a6CKCgx00uCmP+QakBDFXwjAJ69IkkIWHmtmjd3wvXPcdOS44hfX2vqkOQrVrq8l9DhNNADZRXaCEWvgXtVA== - dependencies: - abab "^2.0.6" - acorn "^8.7.1" - acorn-globals "^6.0.0" - cssom "^0.5.0" - cssstyle "^2.3.0" - data-urls "^3.0.2" - decimal.js "^10.3.1" - domexception "^4.0.0" - escodegen "^2.0.0" - form-data "^4.0.0" - html-encoding-sniffer "^3.0.0" - http-proxy-agent "^5.0.0" - https-proxy-agent "^5.0.1" - is-potential-custom-element-name "^1.0.1" - nwsapi "^2.2.0" - parse5 "^7.0.0" - saxes "^6.0.0" - symbol-tree "^3.2.4" - tough-cookie "^4.0.0" - w3c-hr-time "^1.0.2" - w3c-xmlserializer "^3.0.0" - webidl-conversions "^7.0.0" - whatwg-encoding "^2.0.0" - whatwg-mimetype "^3.0.0" - whatwg-url "^11.0.0" - ws "^8.8.0" - xml-name-validator "^4.0.0" - -jsesc@^2.5.1: - version "2.5.2" - resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-2.5.2.tgz#80564d2e483dacf6e8ef209650a67df3f0c283a4" - integrity sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA== - -json-parse-even-better-errors@^2.3.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" - integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== - -json-schema-traverse@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" - integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== - -json-stable-stringify-without-jsonify@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" - integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== - -json5@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe" - integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== - dependencies: - minimist "^1.2.0" - -json5@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/json5/-/json5-2.2.1.tgz#655d50ed1e6f95ad1a3caababd2b0efda10b395c" - integrity sha512-1hqLFMSrGHRHxav9q9gNjJ5EXznIxGVO09xQRrwplcS8qs28pZ8s8hupZAmqDwZUmVZ2Qb2jnyPOWcDH8m8dlA== - -"jsx-ast-utils@^2.4.1 || ^3.0.0", jsx-ast-utils@^3.3.2: - version "3.3.3" - resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-3.3.3.tgz#76b3e6e6cece5c69d49a5792c3d01bd1a0cdc7ea" - integrity sha512-fYQHZTZ8jSfmWZ0iyzfwiU4WDX4HpHbMCZ3gPlWYiCl3BoeOTsqKBqnTVfH2rYT7eP5c3sVbeSPHnnJOaTrWiw== - dependencies: - array-includes "^3.1.5" - object.assign "^4.1.3" - -kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.3" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" - integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== - -kleur@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e" - integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== - -kleur@^4.0.3: - version "4.1.5" - resolved "https://registry.yarnpkg.com/kleur/-/kleur-4.1.5.tgz#95106101795f7050c6c650f350c683febddb1780" - integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ== - -language-subtag-registry@~0.3.2: - version "0.3.22" - resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.22.tgz#2e1500861b2e457eba7e7ae86877cbd08fa1fd1d" - integrity sha512-tN0MCzyWnoz/4nHS6uxdlFWoUZT7ABptwKPQ52Ea7URk6vll88bWBVhodtnlfEuCcKWNGoc+uGbw1cwa9IKh/w== - -language-tags@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/language-tags/-/language-tags-1.0.5.tgz#d321dbc4da30ba8bf3024e040fa5c14661f9193a" - integrity sha512-qJhlO9cGXi6hBGKoxEG/sKZDAHD5Hnu9Hs4WbOY3pCWXDhw0N8x1NenNzm2EnNLkLkk7J2SdxAkDSbb6ftT+UQ== - dependencies: - language-subtag-registry "~0.3.2" - -leven@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" - integrity sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A== - -levn@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" - integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== - dependencies: - prelude-ls "^1.2.1" - type-check "~0.4.0" - -levn@~0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" - integrity sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA== - dependencies: - prelude-ls "~1.1.2" - type-check "~0.3.2" - -lilconfig@^2.0.5, lilconfig@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.6.tgz#32a384558bd58af3d4c6e077dd1ad1d397bc69d4" - integrity sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg== - -lines-and-columns@^1.1.6: - version "1.2.4" - resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" - integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== - -locate-path@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" - integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== - dependencies: - p-locate "^4.1.0" - -locate-path@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" - integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== - dependencies: - p-locate "^5.0.0" - -lodash.castarray@^4.4.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.castarray/-/lodash.castarray-4.4.0.tgz#c02513515e309daddd4c24c60cfddcf5976d9115" - integrity sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q== - -lodash.isplainobject@^4.0.6: - version "4.0.6" - resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" - integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== - -lodash.memoize@4.x: - version "4.1.2" - resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" - integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== - -lodash.merge@^4.6.2: - version "4.6.2" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" - integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== - -lodash@^4.17.15: - version "4.17.21" - resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" - integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== - -longest-streak@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/longest-streak/-/longest-streak-3.0.1.tgz#c97315b7afa0e7d9525db9a5a2953651432bdc5d" - integrity sha512-cHlYSUpL2s7Fb3394mYxwTYj8niTaNHUCLr0qdiCXQfSjfuA7CKofpX2uSwEfFDQ0EB7JcnMnm+GjbqqoinYYg== - -loose-envify@^1.1.0, loose-envify@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" - integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== - dependencies: - js-tokens "^3.0.0 || ^4.0.0" - -lru-cache@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" - integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA== - dependencies: - yallist "^4.0.0" - -lz-string@^1.4.4: - version "1.4.4" - resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" - integrity sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ== - -make-dir@^3.0.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" - integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== - dependencies: - semver "^6.0.0" - -make-error@1.x: - version "1.3.6" - resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" - integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== - -makeerror@1.0.12: - version "1.0.12" - resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.12.tgz#3e5dd2079a82e812e983cc6610c4a2cb0eaa801a" - integrity sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg== - dependencies: - tmpl "1.0.5" - -markdown-extensions@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/markdown-extensions/-/markdown-extensions-1.1.1.tgz#fea03b539faeaee9b4ef02a3769b455b189f7fc3" - integrity sha512-WWC0ZuMzCyDHYCasEGs4IPvLyTGftYwh6wIEOULOF0HXcqZlhwRzrK0w2VUlxWA98xnvb/jszw4ZSkJ6ADpM6Q== - -mdast-util-definitions@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/mdast-util-definitions/-/mdast-util-definitions-5.1.1.tgz#2c1d684b28e53f84938bb06317944bee8efa79db" - integrity sha512-rQ+Gv7mHttxHOBx2dkF4HWTg+EE+UR78ptQWDylzPKaQuVGdG4HIoY3SrS/pCp80nZ04greFvXbVFHT+uf0JVQ== - dependencies: - "@types/mdast" "^3.0.0" - "@types/unist" "^2.0.0" - unist-util-visit "^4.0.0" - -mdast-util-from-markdown@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mdast-util-from-markdown/-/mdast-util-from-markdown-1.2.0.tgz#84df2924ccc6c995dec1e2368b2b208ad0a76268" - integrity sha512-iZJyyvKD1+K7QX1b5jXdE7Sc5dtoTry1vzV28UZZe8Z1xVnB/czKntJ7ZAkG0tANqRnBF6p3p7GpU1y19DTf2Q== - dependencies: - "@types/mdast" "^3.0.0" - "@types/unist" "^2.0.0" - decode-named-character-reference "^1.0.0" - mdast-util-to-string "^3.1.0" - micromark "^3.0.0" - micromark-util-decode-numeric-character-reference "^1.0.0" - micromark-util-decode-string "^1.0.0" - micromark-util-normalize-identifier "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - unist-util-stringify-position "^3.0.0" - uvu "^0.5.0" - -mdast-util-mdx-expression@^1.0.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/mdast-util-mdx-expression/-/mdast-util-mdx-expression-1.3.1.tgz#2224cf0b5b150093704a3c225bd529d2de21f50f" - integrity sha512-TTb6cKyTA1RD+1su1iStZ5PAv3rFfOUKcoU5EstUpv/IZo63uDX03R8+jXjMEhcobXnNOiG6/ccekvVl4eV1zQ== - dependencies: - "@types/estree-jsx" "^1.0.0" - "@types/hast" "^2.0.0" - "@types/mdast" "^3.0.0" - mdast-util-from-markdown "^1.0.0" - mdast-util-to-markdown "^1.0.0" - -mdast-util-mdx-jsx@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-2.1.0.tgz#029f5a9c38485dbb5cf482059557ee7d788f1947" - integrity sha512-KzgzfWMhdteDkrY4mQtyvTU5bc/W4ppxhe9SzelO6QUUiwLAM+Et2Dnjjprik74a336kHdo0zKm7Tp+n6FFeRg== - dependencies: - "@types/estree-jsx" "^1.0.0" - "@types/hast" "^2.0.0" - "@types/mdast" "^3.0.0" - ccount "^2.0.0" - mdast-util-to-markdown "^1.3.0" - parse-entities "^4.0.0" - stringify-entities "^4.0.0" - unist-util-remove-position "^4.0.0" - unist-util-stringify-position "^3.0.0" - vfile-message "^3.0.0" - -mdast-util-mdx@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/mdast-util-mdx/-/mdast-util-mdx-2.0.0.tgz#dd4f6c993cf27da32725e50a04874f595b7b63fb" - integrity sha512-M09lW0CcBT1VrJUaF/PYxemxxHa7SLDHdSn94Q9FhxjCQfuW7nMAWKWimTmA3OyDMSTH981NN1csW1X+HPSluw== - dependencies: - mdast-util-mdx-expression "^1.0.0" - mdast-util-mdx-jsx "^2.0.0" - mdast-util-mdxjs-esm "^1.0.0" - -mdast-util-mdxjs-esm@^1.0.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-1.3.0.tgz#137345ef827169aeeeb6069277cd3e090830ce9a" - integrity sha512-7N5ihsOkAEGjFotIX9p/YPdl4TqUoMxL4ajNz7PbT89BqsdWJuBC9rvgt6wpbwTZqWWR0jKWqQbwsOWDBUZv4g== - dependencies: - "@types/estree-jsx" "^1.0.0" - "@types/hast" "^2.0.0" - "@types/mdast" "^3.0.0" - mdast-util-from-markdown "^1.0.0" - mdast-util-to-markdown "^1.0.0" - -mdast-util-to-hast@^12.1.0: - version "12.2.4" - resolved "https://registry.yarnpkg.com/mdast-util-to-hast/-/mdast-util-to-hast-12.2.4.tgz#34c1ef2b6cf01c27b3e3504e2c977c76f722e7e1" - integrity sha512-a21xoxSef1l8VhHxS1Dnyioz6grrJkoaCUgGzMD/7dWHvboYX3VW53esRUfB5tgTyz4Yos1n25SPcj35dJqmAg== - dependencies: - "@types/hast" "^2.0.0" - "@types/mdast" "^3.0.0" - mdast-util-definitions "^5.0.0" - micromark-util-sanitize-uri "^1.1.0" - trim-lines "^3.0.0" - unist-builder "^3.0.0" - unist-util-generated "^2.0.0" - unist-util-position "^4.0.0" - unist-util-visit "^4.0.0" - -mdast-util-to-markdown@^1.0.0, mdast-util-to-markdown@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-markdown/-/mdast-util-to-markdown-1.3.0.tgz#38b6cdc8dc417de642a469c4fc2abdf8c931bd1e" - integrity sha512-6tUSs4r+KK4JGTTiQ7FfHmVOaDrLQJPmpjD6wPMlHGUVXoG9Vjc3jIeP+uyBWRf8clwB2blM+W7+KrlMYQnftA== - dependencies: - "@types/mdast" "^3.0.0" - "@types/unist" "^2.0.0" - longest-streak "^3.0.0" - mdast-util-to-string "^3.0.0" - micromark-util-decode-string "^1.0.0" - unist-util-visit "^4.0.0" - zwitch "^2.0.0" - -mdast-util-to-string@^3.0.0, mdast-util-to-string@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/mdast-util-to-string/-/mdast-util-to-string-3.1.0.tgz#56c506d065fbf769515235e577b5a261552d56e9" - integrity sha512-n4Vypz/DZgwo0iMHLQL49dJzlp7YtAJP+N07MZHpjPf/5XJuHUWstviF4Mn2jEiR/GNmtnRRqnwsXExk3igfFA== - -mdn-data@2.0.14: - version "2.0.14" - resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.14.tgz#7113fc4281917d63ce29b43446f701e68c25ba50" - integrity sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow== - -merge-stream@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" - integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== - -merge2@^1.3.0, merge2@^1.4.1: - version "1.4.1" - resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" - integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== - -micromark-core-commonmark@^1.0.0, micromark-core-commonmark@^1.0.1: - version "1.0.6" - resolved "https://registry.yarnpkg.com/micromark-core-commonmark/-/micromark-core-commonmark-1.0.6.tgz#edff4c72e5993d93724a3c206970f5a15b0585ad" - integrity sha512-K+PkJTxqjFfSNkfAhp4GB+cZPfQd6dxtTXnf+RjZOV7T4EEXnvgzOcnp+eSTmpGk9d1S9sL6/lqrgSNn/s0HZA== - dependencies: - decode-named-character-reference "^1.0.0" - micromark-factory-destination "^1.0.0" - micromark-factory-label "^1.0.0" - micromark-factory-space "^1.0.0" - micromark-factory-title "^1.0.0" - micromark-factory-whitespace "^1.0.0" - micromark-util-character "^1.0.0" - micromark-util-chunked "^1.0.0" - micromark-util-classify-character "^1.0.0" - micromark-util-html-tag-name "^1.0.0" - micromark-util-normalize-identifier "^1.0.0" - micromark-util-resolve-all "^1.0.0" - micromark-util-subtokenize "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.1" - uvu "^0.5.0" - -micromark-extension-mdx-expression@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/micromark-extension-mdx-expression/-/micromark-extension-mdx-expression-1.0.3.tgz#cd3843573921bf55afcfff4ae0cd2e857a16dcfa" - integrity sha512-TjYtjEMszWze51NJCZmhv7MEBcgYRgb3tJeMAJ+HQCAaZHHRBaDCccqQzGizR/H4ODefP44wRTgOn2vE5I6nZA== - dependencies: - micromark-factory-mdx-expression "^1.0.0" - micromark-factory-space "^1.0.0" - micromark-util-character "^1.0.0" - micromark-util-events-to-acorn "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - uvu "^0.5.0" - -micromark-extension-mdx-jsx@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/micromark-extension-mdx-jsx/-/micromark-extension-mdx-jsx-1.0.3.tgz#9f196be5f65eb09d2a49b237a7b3398bba2999be" - integrity sha512-VfA369RdqUISF0qGgv2FfV7gGjHDfn9+Qfiv5hEwpyr1xscRj/CiVRkU7rywGFCO7JwJ5L0e7CJz60lY52+qOA== - dependencies: - "@types/acorn" "^4.0.0" - estree-util-is-identifier-name "^2.0.0" - micromark-factory-mdx-expression "^1.0.0" - micromark-factory-space "^1.0.0" - micromark-util-character "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - uvu "^0.5.0" - vfile-message "^3.0.0" - -micromark-extension-mdx-md@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-extension-mdx-md/-/micromark-extension-mdx-md-1.0.0.tgz#382f5df9ee3706dd120b51782a211f31f4760d22" - integrity sha512-xaRAMoSkKdqZXDAoSgp20Azm0aRQKGOl0RrS81yGu8Hr/JhMsBmfs4wR7m9kgVUIO36cMUQjNyiyDKPrsv8gOw== - dependencies: - micromark-util-types "^1.0.0" - -micromark-extension-mdxjs-esm@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/micromark-extension-mdxjs-esm/-/micromark-extension-mdxjs-esm-1.0.3.tgz#630d9dc9db2c2fd470cac8c1e7a824851267404d" - integrity sha512-2N13ol4KMoxb85rdDwTAC6uzs8lMX0zeqpcyx7FhS7PxXomOnLactu8WI8iBNXW8AVyea3KIJd/1CKnUmwrK9A== - dependencies: - micromark-core-commonmark "^1.0.0" - micromark-util-character "^1.0.0" - micromark-util-events-to-acorn "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - unist-util-position-from-estree "^1.1.0" - uvu "^0.5.0" - vfile-message "^3.0.0" - -micromark-extension-mdxjs@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-extension-mdxjs/-/micromark-extension-mdxjs-1.0.0.tgz#772644e12fc8299a33e50f59c5aa15727f6689dd" - integrity sha512-TZZRZgeHvtgm+IhtgC2+uDMR7h8eTKF0QUX9YsgoL9+bADBpBY6SiLvWqnBlLbCEevITmTqmEuY3FoxMKVs1rQ== - dependencies: - acorn "^8.0.0" - acorn-jsx "^5.0.0" - micromark-extension-mdx-expression "^1.0.0" - micromark-extension-mdx-jsx "^1.0.0" - micromark-extension-mdx-md "^1.0.0" - micromark-extension-mdxjs-esm "^1.0.0" - micromark-util-combine-extensions "^1.0.0" - micromark-util-types "^1.0.0" - -micromark-factory-destination@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-destination/-/micromark-factory-destination-1.0.0.tgz#fef1cb59ad4997c496f887b6977aa3034a5a277e" - integrity sha512-eUBA7Rs1/xtTVun9TmV3gjfPz2wEwgK5R5xcbIM5ZYAtvGF6JkyaDsj0agx8urXnO31tEO6Ug83iVH3tdedLnw== - dependencies: - micromark-util-character "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - -micromark-factory-label@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/micromark-factory-label/-/micromark-factory-label-1.0.2.tgz#6be2551fa8d13542fcbbac478258fb7a20047137" - integrity sha512-CTIwxlOnU7dEshXDQ+dsr2n+yxpP0+fn271pu0bwDIS8uqfFcumXpj5mLn3hSC8iw2MUr6Gx8EcKng1dD7i6hg== - dependencies: - micromark-util-character "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - uvu "^0.5.0" - -micromark-factory-mdx-expression@^1.0.0: - version "1.0.6" - resolved "https://registry.yarnpkg.com/micromark-factory-mdx-expression/-/micromark-factory-mdx-expression-1.0.6.tgz#917e17d16e6e9c2551f3a862e6a9ebdd22056476" - integrity sha512-WRQIc78FV7KrCfjsEf/sETopbYjElh3xAmNpLkd1ODPqxEngP42eVRGbiPEQWpRV27LzqW+XVTvQAMIIRLPnNA== - dependencies: - micromark-factory-space "^1.0.0" - micromark-util-character "^1.0.0" - micromark-util-events-to-acorn "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - unist-util-position-from-estree "^1.0.0" - uvu "^0.5.0" - vfile-message "^3.0.0" - -micromark-factory-space@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-space/-/micromark-factory-space-1.0.0.tgz#cebff49968f2b9616c0fcb239e96685cb9497633" - integrity sha512-qUmqs4kj9a5yBnk3JMLyjtWYN6Mzfcx8uJfi5XAveBniDevmZasdGBba5b4QsvRcAkmvGo5ACmSUmyGiKTLZew== - dependencies: - micromark-util-character "^1.0.0" - micromark-util-types "^1.0.0" - -micromark-factory-title@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/micromark-factory-title/-/micromark-factory-title-1.0.2.tgz#7e09287c3748ff1693930f176e1c4a328382494f" - integrity sha512-zily+Nr4yFqgMGRKLpTVsNl5L4PMu485fGFDOQJQBl2NFpjGte1e86zC0da93wf97jrc4+2G2GQudFMHn3IX+A== - dependencies: - micromark-factory-space "^1.0.0" - micromark-util-character "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - uvu "^0.5.0" - -micromark-factory-whitespace@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-factory-whitespace/-/micromark-factory-whitespace-1.0.0.tgz#e991e043ad376c1ba52f4e49858ce0794678621c" - integrity sha512-Qx7uEyahU1lt1RnsECBiuEbfr9INjQTGa6Err+gF3g0Tx4YEviPbqqGKNv/NrBaE7dVHdn1bVZKM/n5I/Bak7A== - dependencies: - micromark-factory-space "^1.0.0" - micromark-util-character "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - -micromark-util-character@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/micromark-util-character/-/micromark-util-character-1.1.0.tgz#d97c54d5742a0d9611a68ca0cd4124331f264d86" - integrity sha512-agJ5B3unGNJ9rJvADMJ5ZiYjBRyDpzKAOk01Kpi1TKhlT1APx3XZk6eN7RtSz1erbWHC2L8T3xLZ81wdtGRZzg== - dependencies: - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - -micromark-util-chunked@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-chunked/-/micromark-util-chunked-1.0.0.tgz#5b40d83f3d53b84c4c6bce30ed4257e9a4c79d06" - integrity sha512-5e8xTis5tEZKgesfbQMKRCyzvffRRUX+lK/y+DvsMFdabAicPkkZV6gO+FEWi9RfuKKoxxPwNL+dFF0SMImc1g== - dependencies: - micromark-util-symbol "^1.0.0" - -micromark-util-classify-character@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-classify-character/-/micromark-util-classify-character-1.0.0.tgz#cbd7b447cb79ee6997dd274a46fc4eb806460a20" - integrity sha512-F8oW2KKrQRb3vS5ud5HIqBVkCqQi224Nm55o5wYLzY/9PwHGXC01tr3d7+TqHHz6zrKQ72Okwtvm/xQm6OVNZA== - dependencies: - micromark-util-character "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - -micromark-util-combine-extensions@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-1.0.0.tgz#91418e1e74fb893e3628b8d496085639124ff3d5" - integrity sha512-J8H058vFBdo/6+AsjHp2NF7AJ02SZtWaVUjsayNFeAiydTxUwViQPxN0Hf8dp4FmCQi0UUFovFsEyRSUmFH3MA== - dependencies: - micromark-util-chunked "^1.0.0" - micromark-util-types "^1.0.0" - -micromark-util-decode-numeric-character-reference@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-1.0.0.tgz#dcc85f13b5bd93ff8d2868c3dba28039d490b946" - integrity sha512-OzO9AI5VUtrTD7KSdagf4MWgHMtET17Ua1fIpXTpuhclCqD8egFWo85GxSGvxgkGS74bEahvtM0WP0HjvV0e4w== - dependencies: - micromark-util-symbol "^1.0.0" - -micromark-util-decode-string@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/micromark-util-decode-string/-/micromark-util-decode-string-1.0.2.tgz#942252ab7a76dec2dbf089cc32505ee2bc3acf02" - integrity sha512-DLT5Ho02qr6QWVNYbRZ3RYOSSWWFuH3tJexd3dgN1odEuPNxCngTCXJum7+ViRAd9BbdxCvMToPOD/IvVhzG6Q== - dependencies: - decode-named-character-reference "^1.0.0" - micromark-util-character "^1.0.0" - micromark-util-decode-numeric-character-reference "^1.0.0" - micromark-util-symbol "^1.0.0" - -micromark-util-encode@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-encode/-/micromark-util-encode-1.0.1.tgz#2c1c22d3800870ad770ece5686ebca5920353383" - integrity sha512-U2s5YdnAYexjKDel31SVMPbfi+eF8y1U4pfiRW/Y8EFVCy/vgxk/2wWTxzcqE71LHtCuCzlBDRU2a5CQ5j+mQA== - -micromark-util-events-to-acorn@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/micromark-util-events-to-acorn/-/micromark-util-events-to-acorn-1.2.0.tgz#65785cb77299d791bfefdc6a5213ab57ceead115" - integrity sha512-WWp3bf7xT9MppNuw3yPjpnOxa8cj5ACivEzXJKu0WwnjBYfzaBvIAT9KfeyI0Qkll+bfQtfftSwdgTH6QhTOKw== - dependencies: - "@types/acorn" "^4.0.0" - "@types/estree" "^1.0.0" - estree-util-visit "^1.0.0" - micromark-util-types "^1.0.0" - uvu "^0.5.0" - vfile-location "^4.0.0" - vfile-message "^3.0.0" - -micromark-util-html-tag-name@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-1.1.0.tgz#eb227118befd51f48858e879b7a419fc0df20497" - integrity sha512-BKlClMmYROy9UiV03SwNmckkjn8QHVaWkqoAqzivabvdGcwNGMMMH/5szAnywmsTBUzDsU57/mFi0sp4BQO6dA== - -micromark-util-normalize-identifier@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-1.0.0.tgz#4a3539cb8db954bbec5203952bfe8cedadae7828" - integrity sha512-yg+zrL14bBTFrQ7n35CmByWUTFsgst5JhA4gJYoty4Dqzj4Z4Fr/DHekSS5aLfH9bdlfnSvKAWsAgJhIbogyBg== - dependencies: - micromark-util-symbol "^1.0.0" - -micromark-util-resolve-all@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/micromark-util-resolve-all/-/micromark-util-resolve-all-1.0.0.tgz#a7c363f49a0162e931960c44f3127ab58f031d88" - integrity sha512-CB/AGk98u50k42kvgaMM94wzBqozSzDDaonKU7P7jwQIuH2RU0TeBqGYJz2WY1UdihhjweivStrJ2JdkdEmcfw== - dependencies: - micromark-util-types "^1.0.0" - -micromark-util-sanitize-uri@^1.0.0, micromark-util-sanitize-uri@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-1.1.0.tgz#f12e07a85106b902645e0364feb07cf253a85aee" - integrity sha512-RoxtuSCX6sUNtxhbmsEFQfWzs8VN7cTctmBPvYivo98xb/kDEoTCtJQX5wyzIYEmk/lvNFTat4hL8oW0KndFpg== - dependencies: - micromark-util-character "^1.0.0" - micromark-util-encode "^1.0.0" - micromark-util-symbol "^1.0.0" - -micromark-util-subtokenize@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/micromark-util-subtokenize/-/micromark-util-subtokenize-1.0.2.tgz#ff6f1af6ac836f8bfdbf9b02f40431760ad89105" - integrity sha512-d90uqCnXp/cy4G881Ub4psE57Sf8YD0pim9QdjCRNjfas2M1u6Lbt+XZK9gnHL2XFhnozZiEdCa9CNfXSfQ6xA== - dependencies: - micromark-util-chunked "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.0" - uvu "^0.5.0" - -micromark-util-symbol@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/micromark-util-symbol/-/micromark-util-symbol-1.0.1.tgz#b90344db62042ce454f351cf0bebcc0a6da4920e" - integrity sha512-oKDEMK2u5qqAptasDAwWDXq0tG9AssVwAx3E9bBF3t/shRIGsWIRG+cGafs2p/SnDSOecnt6hZPCE2o6lHfFmQ== - -micromark-util-types@^1.0.0, micromark-util-types@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/micromark-util-types/-/micromark-util-types-1.0.2.tgz#f4220fdb319205812f99c40f8c87a9be83eded20" - integrity sha512-DCfg/T8fcrhrRKTPjRrw/5LLvdGV7BHySf/1LOZx7TzWZdYRjogNtyNq885z3nNallwr3QUKARjqvHqX1/7t+w== - -micromark@^3.0.0: - version "3.0.10" - resolved "https://registry.yarnpkg.com/micromark/-/micromark-3.0.10.tgz#1eac156f0399d42736458a14b0ca2d86190b457c" - integrity sha512-ryTDy6UUunOXy2HPjelppgJ2sNfcPz1pLlMdA6Rz9jPzhLikWXv/irpWV/I2jd68Uhmny7hHxAlAhk4+vWggpg== - dependencies: - "@types/debug" "^4.0.0" - debug "^4.0.0" - decode-named-character-reference "^1.0.0" - micromark-core-commonmark "^1.0.1" - micromark-factory-space "^1.0.0" - micromark-util-character "^1.0.0" - micromark-util-chunked "^1.0.0" - micromark-util-combine-extensions "^1.0.0" - micromark-util-decode-numeric-character-reference "^1.0.0" - micromark-util-encode "^1.0.0" - micromark-util-normalize-identifier "^1.0.0" - micromark-util-resolve-all "^1.0.0" - micromark-util-sanitize-uri "^1.0.0" - micromark-util-subtokenize "^1.0.0" - micromark-util-symbol "^1.0.0" - micromark-util-types "^1.0.1" - uvu "^0.5.0" - -micromatch@^4.0.4, micromatch@^4.0.5: - version "4.0.5" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" - integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== - dependencies: - braces "^3.0.2" - picomatch "^2.3.1" - -mime-db@1.52.0: - version "1.52.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" - integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== - -mime-types@^2.1.12: - version "2.1.35" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" - integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== - dependencies: - mime-db "1.52.0" - -mimic-fn@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" - integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== - -min-indent@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/min-indent/-/min-indent-1.0.1.tgz#a63f681673b30571fbe8bc25686ae746eefa9869" - integrity sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg== - -minimatch@^3.0.4, minimatch@^3.1.1, minimatch@^3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" - -minimist@^1.2.0, minimist@^1.2.6: - version "1.2.6" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" - integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== - -mri@^1.1.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/mri/-/mri-1.2.0.tgz#6721480fec2a11a4889861115a48b6cbe7cc8f0b" - integrity sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA== - -ms@2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" - integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== - -ms@2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" - integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== - -ms@^2.1.1: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - -nano-css@^5.3.1: - version "5.3.5" - resolved "https://registry.yarnpkg.com/nano-css/-/nano-css-5.3.5.tgz#3075ea29ffdeb0c7cb6d25edb21d8f7fa8e8fe8e" - integrity sha512-vSB9X12bbNu4ALBu7nigJgRViZ6ja3OU7CeuiV1zMIbXOdmkLahgtPmh3GBOlDxbKY0CitqlPdOReGlBLSp+yg== - dependencies: - css-tree "^1.1.2" - csstype "^3.0.6" - fastest-stable-stringify "^2.0.2" - inline-style-prefixer "^6.0.0" - rtl-css-js "^1.14.0" - sourcemap-codec "^1.4.8" - stacktrace-js "^2.0.2" - stylis "^4.0.6" - -nanoid@^3.3.4: - version "3.3.4" - resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" - integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== - -natural-compare@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" - integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== - -next-mdx-remote@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/next-mdx-remote/-/next-mdx-remote-4.2.0.tgz#322aaa601d00e34bd27302443c793d432ae16db8" - integrity sha512-X5RhD7f7b78pH2abbuusObSGgII5l54OdusS/2iXljN7WN1cel6ToLlZeCZcyxx9cR4wmBGQYGongIttDYNmAA== - dependencies: - "@mdx-js/mdx" "^2.0.0" - "@mdx-js/react" "^2.0.0" - vfile "^5.3.0" - vfile-matter "^3.0.1" - -next-tick@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.1.0.tgz#1836ee30ad56d67ef281b22bd199f709449b35eb" - integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ== - -next-translate@^1.6.0: - version "1.6.0" - resolved "https://registry.yarnpkg.com/next-translate/-/next-translate-1.6.0.tgz#28329d9970030c573c529a1a6d5c6b4e75146164" - integrity sha512-rmRBYOwPHvokN+5uH3O/SZqfRUVORLkgt/097mSaq3+CWcEtXEjkVrfBZpYwZ8W3og4QVd+uphYX//qegnIbRg== - -next-transpile-modules@^10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/next-transpile-modules/-/next-transpile-modules-10.0.0.tgz#7152880048835acb64d05fc7aa34910cbe7994da" - integrity sha512-FyeJ++Lm2Fq31gbThiRCrJlYpIY9QaI7A3TjuhQLzOix8ChQrvn5ny4MhfIthS5cy6+uK1AhDRvxVdW17y3Xdw== - dependencies: - enhanced-resolve "^5.10.0" - -next@13.0.2: - version "13.0.2" - resolved "https://registry.yarnpkg.com/next/-/next-13.0.2.tgz#b8c8642c70f736ed91105645391d335fc51c8f62" - integrity sha512-uQ5z5e4D9mOe8+upy6bQdYYjo/kk1v3jMW87kTy2TgAyAsEO+CkwRnMgyZ4JoHEnhPZLHwh7dk0XymRNLe1gFw== - dependencies: - "@next/env" "13.0.2" - "@swc/helpers" "0.4.11" - caniuse-lite "^1.0.30001406" - postcss "8.4.14" - styled-jsx "5.1.0" - use-sync-external-store "1.2.0" - optionalDependencies: - "@next/swc-android-arm-eabi" "13.0.2" - "@next/swc-android-arm64" "13.0.2" - "@next/swc-darwin-arm64" "13.0.2" - "@next/swc-darwin-x64" "13.0.2" - "@next/swc-freebsd-x64" "13.0.2" - "@next/swc-linux-arm-gnueabihf" "13.0.2" - "@next/swc-linux-arm64-gnu" "13.0.2" - "@next/swc-linux-arm64-musl" "13.0.2" - "@next/swc-linux-x64-gnu" "13.0.2" - "@next/swc-linux-x64-musl" "13.0.2" - "@next/swc-win32-arm64-msvc" "13.0.2" - "@next/swc-win32-ia32-msvc" "13.0.2" - "@next/swc-win32-x64-msvc" "13.0.2" - -node-fetch@2.6.7: - version "2.6.7" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" - integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== - dependencies: - whatwg-url "^5.0.0" - -node-gyp-build@^4.3.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" - integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== - -node-int64@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" - integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== - -node-releases@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.6.tgz#8a7088c63a55e493845683ebf3c828d8c51c5503" - integrity sha512-PiVXnNuFm5+iYkLBNeq5211hvO38y63T0i2KKh2KnUs3RpzJ+JtODFjkD8yjLwnDkTYF1eKXheUwdssR+NRZdg== - -normalize-path@^3.0.0, normalize-path@~3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" - integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== - -normalize-range@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942" - integrity sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA== - -npm-run-path@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea" - integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw== - dependencies: - path-key "^3.0.0" - -nwsapi@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.1.tgz#10a9f268fbf4c461249ebcfe38e359aa36e2577c" - integrity sha512-JYOWTeFoS0Z93587vRJgASD5Ut11fYl5NyihP3KrYBvMe1FRRs6RN7m20SA/16GM4P6hTnZjT+UmDOt38UeXNg== - -object-assign@^4.1.1: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" - integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== - -object-hash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/object-hash/-/object-hash-3.0.0.tgz#73f97f753e7baffc0e2cc9d6e079079744ac82e9" - integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw== - -object-inspect@^1.12.0, object-inspect@^1.9.0: - version "1.12.2" - resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.2.tgz#c0641f26394532f28ab8d796ab954e43c009a8ea" - integrity sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ== - -object-keys@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" - integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA== - -object.assign@^4.1.2, object.assign@^4.1.3: - version "4.1.3" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.3.tgz#d36b7700ddf0019abb6b1df1bb13f6445f79051f" - integrity sha512-ZFJnX3zltyjcYJL0RoCJuzb+11zWGyaDbjgxZbdV7rFEcHQuYxrZqhow67aA7xpes6LhojyFDaBKAFfogQrikA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - has-symbols "^1.0.3" - object-keys "^1.1.1" - -object.entries@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.5.tgz#e1acdd17c4de2cd96d5a08487cfb9db84d881861" - integrity sha512-TyxmjUoZggd4OrrU1W66FMDG6CuqJxsFvymeyXI51+vQLN67zYfZseptRge703kKQdo4uccgAKebXFcRCzk4+g== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - -object.fromentries@^2.0.5: - version "2.0.5" - resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.5.tgz#7b37b205109c21e741e605727fe8b0ad5fa08251" - integrity sha512-CAyG5mWQRRiBU57Re4FKoTBjXfDoNwdFVH2Y1tS9PqCsfUTymAohOkEMSG3aRNKmv4lV3O7p1et7c187q6bynw== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - -object.hasown@^1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/object.hasown/-/object.hasown-1.1.1.tgz#ad1eecc60d03f49460600430d97f23882cf592a3" - integrity sha512-LYLe4tivNQzq4JdaWW6WO3HMZZJWzkkH8fnI6EebWl0VZth2wL2Lovm74ep2/gZzlaTdV62JZHEqHQ2yVn8Q/A== - dependencies: - define-properties "^1.1.4" - es-abstract "^1.19.5" - -object.values@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac" - integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== - dependencies: - wrappy "1" - -onetime@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" - integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== - dependencies: - mimic-fn "^2.1.0" - -optionator@^0.8.1: - version "0.8.3" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.3.tgz#84fa1d036fe9d3c7e21d99884b601167ec8fb495" - integrity sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA== - dependencies: - deep-is "~0.1.3" - fast-levenshtein "~2.0.6" - levn "~0.3.0" - prelude-ls "~1.1.2" - type-check "~0.3.2" - word-wrap "~1.2.3" - -optionator@^0.9.1: - version "0.9.1" - resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499" - integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw== - dependencies: - deep-is "^0.1.3" - fast-levenshtein "^2.0.6" - levn "^0.4.1" - prelude-ls "^1.2.1" - type-check "^0.4.0" - word-wrap "^1.2.3" - -p-limit@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" - integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== - dependencies: - p-try "^2.0.0" - -p-limit@^3.0.2, p-limit@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" - integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== - dependencies: - yocto-queue "^0.1.0" - -p-locate@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" - integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== - dependencies: - p-limit "^2.2.0" - -p-locate@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" - integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== - dependencies: - p-limit "^3.0.2" - -p-try@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" - integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== - -parent-module@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" - integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== - dependencies: - callsites "^3.0.0" - -parse-entities@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-4.0.0.tgz#f67c856d4e3fe19b1a445c3fabe78dcdc1053eeb" - integrity sha512-5nk9Fn03x3rEhGaX1FU6IDwG/k+GxLXlFAkgrbM1asuAFl3BhdQWvASaIsmwWypRNcZKHPYnIuOSfIWEyEQnPQ== - dependencies: - "@types/unist" "^2.0.0" - character-entities "^2.0.0" - character-entities-legacy "^3.0.0" - character-reference-invalid "^2.0.0" - decode-named-character-reference "^1.0.0" - is-alphanumerical "^2.0.0" - is-decimal "^2.0.0" - is-hexadecimal "^2.0.0" - -parse-json@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-5.2.0.tgz#c76fc66dee54231c962b22bcc8a72cf2f99753cd" - integrity sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg== - dependencies: - "@babel/code-frame" "^7.0.0" - error-ex "^1.3.1" - json-parse-even-better-errors "^2.3.0" - lines-and-columns "^1.1.6" - -parse5@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.0.0.tgz#51f74a5257f5fcc536389e8c2d0b3802e1bfa91a" - integrity sha512-y/t8IXSPWTuRZqXc0ajH/UwDj4mnqLEbSttNbThcFhGrZuOyoyvNBO85PBp2jQa55wY9d07PBNjsK8ZP3K5U6g== - dependencies: - entities "^4.3.0" - -path-exists@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" - integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== - -path-key@^3.0.0, path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-parse@^1.0.7: - version "1.0.7" - resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" - integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== - -path-type@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" - integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== - -periscopic@^3.0.0: - version "3.0.4" - resolved "https://registry.yarnpkg.com/periscopic/-/periscopic-3.0.4.tgz#b3fbed0d1bc844976b977173ca2cd4a0ef4fa8d1" - integrity sha512-SFx68DxCv0Iyo6APZuw/AKewkkThGwssmU0QWtTlvov3VAtPX+QJ4CadwSaz8nrT5jPIuxdvJWB4PnD2KNDxQg== - dependencies: - estree-walker "^3.0.0" - is-reference "^3.0.0" - -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== - -picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.3, picomatch@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" - integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== - -pify@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" - integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog== - -pirates@^4.0.4: - version "4.0.5" - resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b" - integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ== - -pkg-dir@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" - integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== - dependencies: - find-up "^4.0.0" - -postcss-import@^14.1.0: - version "14.1.0" - resolved "https://registry.yarnpkg.com/postcss-import/-/postcss-import-14.1.0.tgz#a7333ffe32f0b8795303ee9e40215dac922781f0" - integrity sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw== - dependencies: - postcss-value-parser "^4.0.0" - read-cache "^1.0.0" - resolve "^1.1.7" - -postcss-js@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/postcss-js/-/postcss-js-4.0.0.tgz#31db79889531b80dc7bc9b0ad283e418dce0ac00" - integrity sha512-77QESFBwgX4irogGVPgQ5s07vLvFqWr228qZY+w6lW599cRlK/HmnlivnnVUxkjHnCu4J16PDMHcH+e+2HbvTQ== - dependencies: - camelcase-css "^2.0.1" - -postcss-load-config@^3.1.4: - version "3.1.4" - resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.4.tgz#1ab2571faf84bb078877e1d07905eabe9ebda855" - integrity sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg== - dependencies: - lilconfig "^2.0.5" - yaml "^1.10.2" - -postcss-nested@5.0.6: - version "5.0.6" - resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-5.0.6.tgz#466343f7fc8d3d46af3e7dba3fcd47d052a945bc" - integrity sha512-rKqm2Fk0KbA8Vt3AdGN0FB9OBOMDVajMG6ZCf/GoHgdxUJ4sBFp0A/uMIRm+MJUdo33YXEtjqIz8u7DAp8B7DA== - dependencies: - postcss-selector-parser "^6.0.6" - -postcss-nested@6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/postcss-nested/-/postcss-nested-6.0.0.tgz#1572f1984736578f360cffc7eb7dca69e30d1735" - integrity sha512-0DkamqrPcmkBDsLn+vQDIrtkSbNkv5AD/M322ySo9kqFkCIYklym2xEmWkwo+Y3/qZo34tzEPNUw4y7yMCdv5w== - dependencies: - postcss-selector-parser "^6.0.10" - -postcss-selector-parser@6.0.10, postcss-selector-parser@^6.0.10, postcss-selector-parser@^6.0.6: - version "6.0.10" - resolved "https://registry.yarnpkg.com/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz#79b61e2c0d1bfc2602d549e11d0876256f8df88d" - integrity sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w== - dependencies: - cssesc "^3.0.0" - util-deprecate "^1.0.2" - -postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2, postcss-value-parser@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" - integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== - -postcss@8.4.14: - version "8.4.14" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.14.tgz#ee9274d5622b4858c1007a74d76e42e56fd21caf" - integrity sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig== - dependencies: - nanoid "^3.3.4" - picocolors "^1.0.0" - source-map-js "^1.0.2" - -postcss@^8.4.14: - version "8.4.16" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.16.tgz#33a1d675fac39941f5f445db0de4db2b6e01d43c" - integrity sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ== - dependencies: - nanoid "^3.3.4" - picocolors "^1.0.0" - source-map-js "^1.0.2" - -postcss@^8.4.18: - version "8.4.18" - resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.18.tgz#6d50046ea7d3d66a85e0e782074e7203bc7fbca2" - integrity sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA== - dependencies: - nanoid "^3.3.4" - picocolors "^1.0.0" - source-map-js "^1.0.2" - -prelude-ls@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" - integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== - -prelude-ls@~1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" - integrity sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w== - -prettier-linter-helpers@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" - integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== - dependencies: - fast-diff "^1.1.2" - -prettier@^2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64" - integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g== - -pretty-format@^27.0.2: - version "27.5.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" - integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== - dependencies: - ansi-regex "^5.0.1" - ansi-styles "^5.0.0" - react-is "^17.0.1" - -pretty-format@^28.0.0, pretty-format@^28.1.3: - version "28.1.3" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-28.1.3.tgz#c9fba8cedf99ce50963a11b27d982a9ae90970d5" - integrity sha512-8gFb/To0OmxHR9+ZTb14Df2vNxdGCX8g1xWGUTqUw5TiZvcQf5sHKObd5UcPyLLyowNwDAMTF3XWOG1B6mxl1Q== - dependencies: - "@jest/schemas" "^28.1.3" - ansi-regex "^5.0.1" - ansi-styles "^5.0.0" - react-is "^18.0.0" - -pretty-format@^29.0.0, pretty-format@^29.0.3: - version "29.0.3" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.0.3.tgz#23d5f8cabc9cbf209a77d49409d093d61166a811" - integrity sha512-cHudsvQr1K5vNVLbvYF/nv3Qy/F/BcEKxGuIeMiVMRHxPOO1RxXooP8g/ZrwAp7Dx+KdMZoOc7NxLHhMrP2f9Q== - dependencies: - "@jest/schemas" "^29.0.0" - ansi-styles "^5.0.0" - react-is "^18.0.0" - -pretty-format@^29.2.1: - version "29.2.1" - resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-29.2.1.tgz#86e7748fe8bbc96a6a4e04fa99172630907a9611" - integrity sha512-Y41Sa4aLCtKAXvwuIpTvcFBkyeYp2gdFWzXGA+ZNES3VwURIB165XO/z7CjETwzCCS53MjW/rLMyyqEnTtaOfA== - dependencies: - "@jest/schemas" "^29.0.0" - ansi-styles "^5.0.0" - react-is "^18.0.0" - -promise-polyfill@^8.1.3: - version "8.2.3" - resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-8.2.3.tgz#2edc7e4b81aff781c88a0d577e5fe9da822107c6" - integrity sha512-Og0+jCRQetV84U8wVjMNccfGCnMQ9mGs9Hv78QFe+pSDD3gWTpz0y+1QCuxy5d/vBFuZ3iwP2eycAkvqIMPmWg== - -prompts@^2.0.1: - version "2.4.2" - resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.4.2.tgz#7b57e73b3a48029ad10ebd44f74b01722a4cb069" - integrity sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q== - dependencies: - kleur "^3.0.3" - sisteransi "^1.0.5" - -prop-types@^15.8.1: - version "15.8.1" - resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" - integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== - dependencies: - loose-envify "^1.4.0" - object-assign "^4.1.1" - react-is "^16.13.1" - -property-information@^6.0.0: - version "6.1.1" - resolved "https://registry.yarnpkg.com/property-information/-/property-information-6.1.1.tgz#5ca85510a3019726cb9afed4197b7b8ac5926a22" - integrity sha512-hrzC564QIl0r0vy4l6MvRLhafmUowhO/O3KgVSoXIbbA2Sz4j8HGpJc6T2cubRVwMwpdiG/vKGfhT4IixmKN9w== - -psl@^1.1.33: - version "1.9.0" - resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" - integrity sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag== - -punycode@^2.1.0, punycode@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec" - integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A== - -qs@^6.11.0: - version "6.11.0" - resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" - integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== - dependencies: - side-channel "^1.0.4" - -queue-microtask@^1.2.2: - version "1.2.3" - resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" - integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== - -quick-lru@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-5.1.1.tgz#366493e6b3e42a3a6885e2e99d18f80fb7a8c932" - integrity sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA== - -react-daisyui@^2.4.6: - version "2.4.6" - resolved "https://registry.yarnpkg.com/react-daisyui/-/react-daisyui-2.4.6.tgz#955a84c7638e3c071482f0e8096282da01ad67fe" - integrity sha512-T1BoSwVjy6hbmwDRy1eikE6vBIWRfLam60uyyXuC807cji3WZK6YBEoi8NxeoUuWVWmNbzC3CDjZ7+FFleYQxg== - -react-dom@^18.2.0: - version "18.2.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d" - integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== - dependencies: - loose-envify "^1.1.0" - scheduler "^0.23.0" - -react-hook-form@^7.39.1: - version "7.39.1" - resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.39.1.tgz#ded87d4b3f6692d1f9219515f78ca282b6e1ebf7" - integrity sha512-MiF9PCILN5KulhSGbnjohMiTOrB47GerDTichMNP0y2cPUu1GTRFqbunOxCE9N1499YTLMV/ne4gFzqCp1rxrQ== - -react-is@^16.13.1: - version "16.13.1" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" - integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== - -react-is@^17.0.1: - version "17.0.2" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" - integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== - -react-is@^18.0.0: - version "18.2.0" - resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b" - integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w== - -react-toastify@^9.1.1: - version "9.1.1" - resolved "https://registry.yarnpkg.com/react-toastify/-/react-toastify-9.1.1.tgz#9280caea4a13dc1739c350d90660a630807bf10b" - integrity sha512-pkFCla1z3ve045qvjEmn2xOJOy4ZciwRXm1oMPULVkELi5aJdHCN/FHnuqXq8IwGDLB7PPk2/J6uP9D8ejuiRw== - dependencies: - clsx "^1.1.1" - -react-universal-interface@^0.6.2: - version "0.6.2" - resolved "https://registry.yarnpkg.com/react-universal-interface/-/react-universal-interface-0.6.2.tgz#5e8d438a01729a4dbbcbeeceb0b86be146fe2b3b" - integrity sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw== - -react-use@^17.4.0: - version "17.4.0" - resolved "https://registry.yarnpkg.com/react-use/-/react-use-17.4.0.tgz#cefef258b0a6c534a5c8021c2528ac6e1a4cdc6d" - integrity sha512-TgbNTCA33Wl7xzIJegn1HndB4qTS9u03QUwyNycUnXaweZkE4Kq2SB+Yoxx8qbshkZGYBDvUXbXWRUmQDcZZ/Q== - dependencies: - "@types/js-cookie" "^2.2.6" - "@xobotyi/scrollbar-width" "^1.9.5" - copy-to-clipboard "^3.3.1" - fast-deep-equal "^3.1.3" - fast-shallow-equal "^1.0.0" - js-cookie "^2.2.1" - nano-css "^5.3.1" - react-universal-interface "^0.6.2" - resize-observer-polyfill "^1.5.1" - screenfull "^5.1.0" - set-harmonic-interval "^1.0.1" - throttle-debounce "^3.0.1" - ts-easing "^0.2.0" - tslib "^2.1.0" - -react@^18.2.0: - version "18.2.0" - resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5" - integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== - dependencies: - loose-envify "^1.1.0" - -read-cache@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/read-cache/-/read-cache-1.0.0.tgz#e664ef31161166c9751cdbe8dbcf86b5fb58f774" - integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA== - dependencies: - pify "^2.3.0" - -readdirp@~3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" - integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== - dependencies: - picomatch "^2.2.1" - -redent@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/redent/-/redent-3.0.0.tgz#e557b7998316bb53c9f1f56fa626352c6963059f" - integrity sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg== - dependencies: - indent-string "^4.0.0" - strip-indent "^3.0.0" - -regenerator-runtime@^0.13.4: - version "0.13.9" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz#8925742a98ffd90814988d7566ad30ca3b263b52" - integrity sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA== - -regexp.prototype.flags@^1.4.1, regexp.prototype.flags@^1.4.3: - version "1.4.3" - resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz#87cab30f80f66660181a3bb7bf5981a872b367ac" - integrity sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - functions-have-names "^1.2.2" - -regexpp@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2" - integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg== - -remark-mdx@^2.0.0: - version "2.1.3" - resolved "https://registry.yarnpkg.com/remark-mdx/-/remark-mdx-2.1.3.tgz#6273e8b94d27ade35407a63bc8cdd04592f7be9f" - integrity sha512-3SmtXOy9+jIaVctL8Cs3VAQInjRLGOwNXfrBB9KCT+EpJpKD3PQiy0x8hUNGyjQmdyOs40BqgPU7kYtH9uoR6w== - dependencies: - mdast-util-mdx "^2.0.0" - micromark-extension-mdxjs "^1.0.0" - -remark-parse@^10.0.0: - version "10.0.1" - resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-10.0.1.tgz#6f60ae53edbf0cf38ea223fe643db64d112e0775" - integrity sha512-1fUyHr2jLsVOkhbvPRBJ5zTKZZyD6yZzYaWCS6BPBdQ8vEMBCH+9zNCDA6tET/zHCi/jLqjCWtlJZUPk+DbnFw== - dependencies: - "@types/mdast" "^3.0.0" - mdast-util-from-markdown "^1.0.0" - unified "^10.0.0" - -remark-rehype@^10.0.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/remark-rehype/-/remark-rehype-10.1.0.tgz#32dc99d2034c27ecaf2e0150d22a6dcccd9a6279" - integrity sha512-EFmR5zppdBp0WQeDVZ/b66CWJipB2q2VLNFMabzDSGR66Z2fQii83G5gTBbgGEnEEA0QRussvrFHxk1HWGJskw== - dependencies: - "@types/hast" "^2.0.0" - "@types/mdast" "^3.0.0" - mdast-util-to-hast "^12.1.0" - unified "^10.0.0" - -require-directory@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" - integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== - -resize-observer-polyfill@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz#0e9020dd3d21024458d4ebd27e23e40269810464" - integrity sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg== - -resolve-cwd@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" - integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== - dependencies: - resolve-from "^5.0.0" - -resolve-from@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" - integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== - -resolve-from@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" - integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== - -resolve.exports@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-1.1.0.tgz#5ce842b94b05146c0e03076985d1d0e7e48c90c9" - integrity sha512-J1l+Zxxp4XK3LUDZ9m60LRJF/mAe4z6a4xyabPHk7pvK5t35dACV32iIjJDFeWZFfZlO29w6SZ67knR0tHzJtQ== - -resolve@^1.1.7, resolve@^1.20.0, resolve@^1.22.0, resolve@^1.22.1: - version "1.22.1" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" - integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== - dependencies: - is-core-module "^2.9.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -resolve@^2.0.0-next.3: - version "2.0.0-next.4" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-2.0.0-next.4.tgz#3d37a113d6429f496ec4752d2a2e58efb1fd4660" - integrity sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ== - dependencies: - is-core-module "^2.9.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -reusify@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" - integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== - -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - -rtl-css-js@^1.14.0: - version "1.16.0" - resolved "https://registry.yarnpkg.com/rtl-css-js/-/rtl-css-js-1.16.0.tgz#e8d682982441aadb63cabcb2f7385f3fb78ff26e" - integrity sha512-Oc7PnzwIEU4M0K1J4h/7qUUaljXhQ0kCObRsZjxs2HjkpKsnoTMvSmvJ4sqgJZd0zBoEfAyTdnK/jMIYvrjySQ== - dependencies: - "@babel/runtime" "^7.1.2" - -run-parallel@^1.1.9: - version "1.2.0" - resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" - integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== - dependencies: - queue-microtask "^1.2.2" - -sade@^1.7.3: - version "1.8.1" - resolved "https://registry.yarnpkg.com/sade/-/sade-1.8.1.tgz#0a78e81d658d394887be57d2a409bf703a3b2701" - integrity sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A== - dependencies: - mri "^1.1.0" - -safe-buffer@~5.1.1: - version "5.1.2" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" - integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== - -"safer-buffer@>= 2.1.2 < 3.0.0": - version "2.1.2" - resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" - integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== - -satori@0.0.43: - version "0.0.43" - resolved "https://registry.yarnpkg.com/satori/-/satori-0.0.43.tgz#865b80feed473fc254e389fa7c0bd4c1d8e9aad7" - integrity sha512-SzYwr+LsELWRJU9KMviEOE9TdShry+R5AdS54YQvgAVKFDN4yniAIzwQk1/z2TtIx0ceUT9zTeosWAoWvJBEtQ== - dependencies: - "@shuding/opentype.js" "1.4.0-beta.0" - css-background-parser "^0.1.0" - css-box-shadow "1.0.0-3" - css-to-react-native "^3.0.0" - emoji-regex "^10.2.1" - postcss-value-parser "^4.2.0" - yoga-layout-prebuilt "^1.10.0" - -saxes@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/saxes/-/saxes-6.0.0.tgz#fe5b4a4768df4f14a201b1ba6a65c1f3d9988cc5" - integrity sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA== - dependencies: - xmlchars "^2.2.0" - -scheduler@^0.23.0: - version "0.23.0" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe" - integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw== - dependencies: - loose-envify "^1.1.0" - -screenfull@^5.1.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/screenfull/-/screenfull-5.2.0.tgz#6533d524d30621fc1283b9692146f3f13a93d1ba" - integrity sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA== - -section-matter@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/section-matter/-/section-matter-1.0.0.tgz#e9041953506780ec01d59f292a19c7b850b84167" - integrity sha512-vfD3pmTzGpufjScBh50YHKzEu2lxBWhVEHsNGoEXmCmn2hKGfeNLYMzCJpe8cD7gqX7TJluOVpBkAequ6dgMmA== - dependencies: - extend-shallow "^2.0.1" - kind-of "^6.0.0" - -semver@7.x, semver@^7.3.5, semver@^7.3.7: - version "7.3.7" - resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.7.tgz#12c5b649afdbf9049707796e22a4028814ce523f" - integrity sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g== - dependencies: - lru-cache "^6.0.0" - -semver@^6.0.0, semver@^6.3.0: - version "6.3.0" - resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" - integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== - -set-harmonic-interval@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz#e1773705539cdfb80ce1c3d99e7f298bb3995249" - integrity sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g== - -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - -side-channel@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf" - integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw== - dependencies: - call-bind "^1.0.0" - get-intrinsic "^1.0.2" - object-inspect "^1.9.0" - -signal-exit@^3.0.3, signal-exit@^3.0.7: - version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" - integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== - -simple-swizzle@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.2.tgz#a4da6b635ffcccca33f70d17cb92592de95e557a" - integrity sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg== - dependencies: - is-arrayish "^0.3.1" - -sisteransi@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed" - integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg== - -slash@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" - integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== - -source-map-js@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" - integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== - -source-map-support@0.5.13: - version "0.5.13" - resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.13.tgz#31b24a9c2e73c2de85066c0feb7d44767ed52932" - integrity sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w== - dependencies: - buffer-from "^1.0.0" - source-map "^0.6.0" - -source-map@0.5.6: - version "0.5.6" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" - integrity sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA== - -source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.1: - version "0.6.1" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" - integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== - -source-map@^0.7.0: - version "0.7.4" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656" - integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== - -sourcemap-codec@^1.4.8: - version "1.4.8" - resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" - integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== - -space-separated-tokens@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-2.0.1.tgz#43193cec4fb858a2ce934b7f98b7f2c18107098b" - integrity sha512-ekwEbFp5aqSPKaqeY1PGrlGQxPNaq+Cnx4+bE2D8sciBQrHpbwoBbawqTN2+6jPs9IdWxxiUcN0K2pkczD3zmw== - -sprintf-js@~1.0.2: - version "1.0.3" - resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" - integrity sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g== - -stack-generator@^2.0.5: - version "2.0.10" - resolved "https://registry.yarnpkg.com/stack-generator/-/stack-generator-2.0.10.tgz#8ae171e985ed62287d4f1ed55a1633b3fb53bb4d" - integrity sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ== - dependencies: - stackframe "^1.3.4" - -stack-utils@^2.0.3: - version "2.0.5" - resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-2.0.5.tgz#d25265fca995154659dbbfba3b49254778d2fdd5" - integrity sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA== - dependencies: - escape-string-regexp "^2.0.0" - -stackframe@^1.3.4: - version "1.3.4" - resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.3.4.tgz#b881a004c8c149a5e8efef37d51b16e412943310" - integrity sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw== - -stacktrace-gps@^3.0.4: - version "3.1.2" - resolved "https://registry.yarnpkg.com/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz#0c40b24a9b119b20da4525c398795338966a2fb0" - integrity sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ== - dependencies: - source-map "0.5.6" - stackframe "^1.3.4" - -stacktrace-js@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/stacktrace-js/-/stacktrace-js-2.0.2.tgz#4ca93ea9f494752d55709a081d400fdaebee897b" - integrity sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg== - dependencies: - error-stack-parser "^2.0.6" - stack-generator "^2.0.5" - stacktrace-gps "^3.0.4" - -string-length@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" - integrity sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ== - dependencies: - char-regex "^1.0.2" - strip-ansi "^6.0.0" - -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string.prototype.codepointat@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/string.prototype.codepointat/-/string.prototype.codepointat-0.2.1.tgz#004ad44c8afc727527b108cd462b4d971cd469bc" - integrity sha512-2cBVCj6I4IOvEnjgO/hWqXjqBGsY+zwPmHl12Srk9IXSZ56Jwwmy+66XO5Iut/oQVR7t5ihYdLB0GMa4alEUcg== - -string.prototype.matchall@^4.0.7: - version "4.0.7" - resolved "https://registry.yarnpkg.com/string.prototype.matchall/-/string.prototype.matchall-4.0.7.tgz#8e6ecb0d8a1fb1fda470d81acecb2dba057a481d" - integrity sha512-f48okCX7JiwVi1NXCVWcFnZgADDC/n2vePlQ/KUCNqCikLLilQvwjMO8+BHVKvgzH0JB0J9LEPgxOGT02RoETg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.3" - es-abstract "^1.19.1" - get-intrinsic "^1.1.1" - has-symbols "^1.0.3" - internal-slot "^1.0.3" - regexp.prototype.flags "^1.4.1" - side-channel "^1.0.4" - -string.prototype.trimend@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.5.tgz#914a65baaab25fbdd4ee291ca7dde57e869cb8d0" - integrity sha512-I7RGvmjV4pJ7O3kdf+LXFpVfdNOxtCW/2C8f6jNiW4+PQchwxkCDzlk1/7p+Wl4bqFIZeF47qAHXLuHHWKAxog== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.19.5" - -string.prototype.trimstart@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.5.tgz#5466d93ba58cfa2134839f81d7f42437e8c01fef" - integrity sha512-THx16TJCGlsN0o6dl2o6ncWUsdgnLRSA23rRE5pyGBw/mLr3Ej/R2LaqCtgP8VNMGZsvMWnf9ooZPyY2bHvUFg== - dependencies: - call-bind "^1.0.2" - define-properties "^1.1.4" - es-abstract "^1.19.5" - -stringify-entities@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/stringify-entities/-/stringify-entities-4.0.3.tgz#cfabd7039d22ad30f3cc435b0ca2c1574fc88ef8" - integrity sha512-BP9nNHMhhfcMbiuQKCqMjhDP5yBCAxsPu4pHFFzJ6Alo9dZgY4VLDPutXqIjpRiMoKdp7Av85Gr73Q5uH9k7+g== - dependencies: - character-entities-html4 "^2.0.0" - character-entities-legacy "^3.0.0" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-bom-string@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/strip-bom-string/-/strip-bom-string-1.0.0.tgz#e5211e9224369fbb81d633a2f00044dc8cedad92" - integrity sha512-uCC2VHvQRYu+lMh4My/sFNmF2klFymLX1wHJeXnbEJERpV/ZsVuonzerjfrGpIGF7LBVa1O7i9kjiWvJiFck8g== - -strip-bom@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3" - integrity sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA== - -strip-bom@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-4.0.0.tgz#9c3505c1db45bcedca3d9cf7a16f5c5aa3901878" - integrity sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w== - -strip-final-newline@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" - integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== - -strip-indent@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/strip-indent/-/strip-indent-3.0.0.tgz#c32e1cee940b6b3432c771bc2c54bcce73cd3001" - integrity sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ== - dependencies: - min-indent "^1.0.0" - -strip-json-comments@^3.1.0, strip-json-comments@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" - integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== - -stripe@^10.16.0: - version "10.16.0" - resolved "https://registry.yarnpkg.com/stripe/-/stripe-10.16.0.tgz#bfa619dc8ef84b1beb9ce8c2239ab9f6954047bb" - integrity sha512-nae4WV8W8xJ8KpjRx3aUD9FavoCQ98F1kaF5v0ltNHoNbtwIPTgrfmq92J0PXKDvb4TOd0tcXXkR5LG3UxjxGw== - dependencies: - "@types/node" ">=8.1.0" - qs "^6.11.0" - -style-to-object@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/style-to-object/-/style-to-object-0.3.0.tgz#b1b790d205991cc783801967214979ee19a76e46" - integrity sha512-CzFnRRXhzWIdItT3OmF8SQfWyahHhjq3HwcMNCNLn+N7klOOqPjMeG/4JSu77D7ypZdGvSzvkrbyeTMizz2VrA== - dependencies: - inline-style-parser "0.1.1" - -styled-jsx@5.1.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/styled-jsx/-/styled-jsx-5.1.0.tgz#4a5622ab9714bd3fcfaeec292aa555871f057563" - integrity sha512-/iHaRJt9U7T+5tp6TRelLnqBqiaIT0HsO0+vgyj8hK2KUk7aejFqRrumqPUlAqDwAj8IbS/1hk3IhBAAK/FCUQ== - dependencies: - client-only "0.0.1" - -stylis@^4.0.6: - version "4.1.1" - resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.1.1.tgz#e46c6a9bbf7c58db1e65bb730be157311ae1fe12" - integrity sha512-lVrM/bNdhVX2OgBFNa2YJ9Lxj7kPzylieHd3TNjuGE0Re9JB7joL5VUKOVH1kdNNJTgGPpT8hmwIAPLaSyEVFQ== - -supports-color@^5.3.0: - version "5.5.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" - integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow== - dependencies: - has-flag "^3.0.0" - -supports-color@^7.1.0: - version "7.2.0" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" - integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== - dependencies: - has-flag "^4.0.0" - -supports-color@^8.0.0: - version "8.1.1" - resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" - integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== - dependencies: - has-flag "^4.0.0" - -supports-preserve-symlinks-flag@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" - integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== - -symbol-tree@^3.2.4: - version "3.2.4" - resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2" - integrity sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw== - -tailwind-merge@^1.8.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/tailwind-merge/-/tailwind-merge-1.8.0.tgz#41c4730b2a51ca7a905c6beebcbfe237bfb61f30" - integrity sha512-tER/2SbYRdfPYg6m4pDWZSlbymLTmDi+dx4iCsJmgmz4UDGzgnVelOvBe3GNtGCw9Bmc4MiObfJJbKeVL+KnMQ== - -tailwindcss@^3: - version "3.1.8" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.1.8.tgz#4f8520550d67a835d32f2f4021580f9fddb7b741" - integrity sha512-YSneUCZSFDYMwk+TGq8qYFdCA3yfBRdBlS7txSq0LUmzyeqRe3a8fBQzbz9M3WS/iFT4BNf/nmw9mEzrnSaC0g== - dependencies: - arg "^5.0.2" - chokidar "^3.5.3" - color-name "^1.1.4" - detective "^5.2.1" - didyoumean "^1.2.2" - dlv "^1.1.3" - fast-glob "^3.2.11" - glob-parent "^6.0.2" - is-glob "^4.0.3" - lilconfig "^2.0.6" - normalize-path "^3.0.0" - object-hash "^3.0.0" - picocolors "^1.0.0" - postcss "^8.4.14" - postcss-import "^14.1.0" - postcss-js "^4.0.0" - postcss-load-config "^3.1.4" - postcss-nested "5.0.6" - postcss-selector-parser "^6.0.10" - postcss-value-parser "^4.2.0" - quick-lru "^5.1.1" - resolve "^1.22.1" - -tailwindcss@^3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/tailwindcss/-/tailwindcss-3.2.2.tgz#705f78cec8f4de2feb52abdb7a8a056e67f2d736" - integrity sha512-c2GtSdqg+harR4QeoTmex0Ngfg8IIHNeLQH5yr2B9uZbZR1Xt1rYbjWOWTcj3YLTZhrmZnPowoQDbSRFyZHQ5Q== - dependencies: - arg "^5.0.2" - chokidar "^3.5.3" - color-name "^1.1.4" - detective "^5.2.1" - didyoumean "^1.2.2" - dlv "^1.1.3" - fast-glob "^3.2.12" - glob-parent "^6.0.2" - is-glob "^4.0.3" - lilconfig "^2.0.6" - micromatch "^4.0.5" - normalize-path "^3.0.0" - object-hash "^3.0.0" - picocolors "^1.0.0" - postcss "^8.4.18" - postcss-import "^14.1.0" - postcss-js "^4.0.0" - postcss-load-config "^3.1.4" - postcss-nested "6.0.0" - postcss-selector-parser "^6.0.10" - postcss-value-parser "^4.2.0" - quick-lru "^5.1.1" - resolve "^1.22.1" - -tapable@^2.2.0: - version "2.2.1" - resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" - integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== - -test-exclude@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/test-exclude/-/test-exclude-6.0.0.tgz#04a8698661d805ea6fa293b6cb9e63ac044ef15e" - integrity sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w== - dependencies: - "@istanbuljs/schema" "^0.1.2" - glob "^7.1.4" - minimatch "^3.0.4" - -text-table@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" - integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== - -throttle-debounce@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-3.0.1.tgz#32f94d84dfa894f786c9a1f290e7a645b6a19abb" - integrity sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg== - -tmpl@1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.5.tgz#8683e0b902bb9c20c4f726e3c0b69f36518c07cc" - integrity sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw== - -to-fast-properties@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e" - integrity sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog== - -to-regex-range@^5.0.1: - version "5.0.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" - integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== - dependencies: - is-number "^7.0.0" - -toggle-selection@^1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" - integrity sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ== - -tough-cookie@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" - integrity sha512-tHdtEpQCMrc1YLrMaqXXcj6AxhYi/xgit6mZu1+EDWUn+qhUf8wMQoFIy9NXuq23zAwtcB0t/MjACGR18pcRbg== - dependencies: - psl "^1.1.33" - punycode "^2.1.1" - universalify "^0.1.2" - -tr46@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-3.0.0.tgz#555c4e297a950617e8eeddef633c87d4d9d6cbf9" - integrity sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA== - dependencies: - punycode "^2.1.1" - -tr46@~0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a" - integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw== - -trim-lines@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/trim-lines/-/trim-lines-3.0.1.tgz#d802e332a07df861c48802c04321017b1bd87338" - integrity sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg== - -trough@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/trough/-/trough-2.1.0.tgz#0f7b511a4fde65a46f18477ab38849b22c554876" - integrity sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g== - -ts-easing@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/ts-easing/-/ts-easing-0.2.0.tgz#c8a8a35025105566588d87dbda05dd7fbfa5a4ec" - integrity sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ== - -ts-jest@^29.0.3: - version "29.0.3" - resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.0.3.tgz#63ea93c5401ab73595440733cefdba31fcf9cb77" - integrity sha512-Ibygvmuyq1qp/z3yTh9QTwVVAbFdDy/+4BtIQR2sp6baF2SJU/8CKK/hhnGIDY2L90Az2jIqTwZPnN2p+BweiQ== - dependencies: - bs-logger "0.x" - fast-json-stable-stringify "2.x" - jest-util "^29.0.0" - json5 "^2.2.1" - lodash.memoize "4.x" - make-error "1.x" - semver "7.x" - yargs-parser "^21.0.1" - -tsconfig-paths@^3.14.1: - version "3.14.1" - resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a" - integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ== - dependencies: - "@types/json5" "^0.0.29" - json5 "^1.0.1" - minimist "^1.2.6" - strip-bom "^3.0.0" - -tslib@^1.8.1: - version "1.14.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" - integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== - -tslib@^2.1.0, tslib@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" - integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== - -tsutils@^3.21.0: - version "3.21.0" - resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" - integrity sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA== - dependencies: - tslib "^1.8.1" - -type-check@^0.4.0, type-check@~0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" - integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== - dependencies: - prelude-ls "^1.2.1" - -type-check@~0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" - integrity sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg== - dependencies: - prelude-ls "~1.1.2" - -type-detect@4.0.8: - version "4.0.8" - resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c" - integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g== - -type-fest@^0.20.2: - version "0.20.2" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" - integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== - -type-fest@^0.21.3: - version "0.21.3" - resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" - integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== - -type@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" - integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== - -type@^2.5.0: - version "2.7.2" - resolved "https://registry.yarnpkg.com/type/-/type-2.7.2.tgz#2376a15a3a28b1efa0f5350dcf72d24df6ef98d0" - integrity sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw== - -typedarray-to-buffer@^3.1.5: - version "3.1.5" - resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" - integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== - dependencies: - is-typedarray "^1.0.0" - -typescript@^4.8.4: - version "4.8.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" - integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== - -unbox-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.2.tgz#29032021057d5e6cdbd08c5129c226dff8ed6f9e" - integrity sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw== - dependencies: - call-bind "^1.0.2" - has-bigints "^1.0.2" - has-symbols "^1.0.3" - which-boxed-primitive "^1.0.2" - -unified@^10.0.0: - version "10.1.2" - resolved "https://registry.yarnpkg.com/unified/-/unified-10.1.2.tgz#b1d64e55dafe1f0b98bb6c719881103ecf6c86df" - integrity sha512-pUSWAi/RAnVy1Pif2kAoeWNBa3JVrx0MId2LASj8G+7AiHWoKZNTomq6LG326T68U7/e263X6fTdcXIy7XnF7Q== - dependencies: - "@types/unist" "^2.0.0" - bail "^2.0.0" - extend "^3.0.0" - is-buffer "^2.0.0" - is-plain-obj "^4.0.0" - trough "^2.0.0" - vfile "^5.0.0" - -unist-builder@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/unist-builder/-/unist-builder-3.0.0.tgz#728baca4767c0e784e1e64bb44b5a5a753021a04" - integrity sha512-GFxmfEAa0vi9i5sd0R2kcrI9ks0r82NasRq5QHh2ysGngrc6GiqD5CDf1FjPenY4vApmFASBIIlk/jj5J5YbmQ== - dependencies: - "@types/unist" "^2.0.0" - -unist-util-generated@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/unist-util-generated/-/unist-util-generated-2.0.0.tgz#86fafb77eb6ce9bfa6b663c3f5ad4f8e56a60113" - integrity sha512-TiWE6DVtVe7Ye2QxOVW9kqybs6cZexNwTwSMVgkfjEReqy/xwGpAXb99OxktoWwmL+Z+Epb0Dn8/GNDYP1wnUw== - -unist-util-is@^5.0.0: - version "5.1.1" - resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-5.1.1.tgz#e8aece0b102fa9bc097b0fef8f870c496d4a6236" - integrity sha512-F5CZ68eYzuSvJjGhCLPL3cYx45IxkqXSetCcRgUXtbcm50X2L9oOWQlfUfDdAf+6Pd27YDblBfdtmsThXmwpbQ== - -unist-util-position-from-estree@^1.0.0, unist-util-position-from-estree@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/unist-util-position-from-estree/-/unist-util-position-from-estree-1.1.1.tgz#96f4d543dfb0428edc01ebb928570b602d280c4c" - integrity sha512-xtoY50b5+7IH8tFbkw64gisG9tMSpxDjhX9TmaJJae/XuxQ9R/Kc8Nv1eOsf43Gt4KV/LkriMy9mptDr7XLcaw== - dependencies: - "@types/unist" "^2.0.0" - -unist-util-position@^4.0.0: - version "4.0.3" - resolved "https://registry.yarnpkg.com/unist-util-position/-/unist-util-position-4.0.3.tgz#5290547b014f6222dff95c48d5c3c13a88fadd07" - integrity sha512-p/5EMGIa1qwbXjA+QgcBXaPWjSnZfQ2Sc3yBEEfgPwsEmJd8Qh+DSk3LGnmOM4S1bY2C0AjmMnB8RuEYxpPwXQ== - dependencies: - "@types/unist" "^2.0.0" - -unist-util-remove-position@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-4.0.1.tgz#d5b46a7304ac114c8d91990ece085ca7c2c135c8" - integrity sha512-0yDkppiIhDlPrfHELgB+NLQD5mfjup3a8UYclHruTJWmY74je8g+CIFr79x5f6AkmzSwlvKLbs63hC0meOMowQ== - dependencies: - "@types/unist" "^2.0.0" - unist-util-visit "^4.0.0" - -unist-util-stringify-position@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-3.0.2.tgz#5c6aa07c90b1deffd9153be170dce628a869a447" - integrity sha512-7A6eiDCs9UtjcwZOcCpM4aPII3bAAGv13E96IkawkOAW0OhH+yRxtY0lzo8KiHpzEMfH7Q+FizUmwp8Iqy5EWg== - dependencies: - "@types/unist" "^2.0.0" - -unist-util-visit-parents@^5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/unist-util-visit-parents/-/unist-util-visit-parents-5.1.1.tgz#868f353e6fce6bf8fa875b251b0f4fec3be709bb" - integrity sha512-gks4baapT/kNRaWxuGkl5BIhoanZo7sC/cUT/JToSRNL1dYoXRFl75d++NkjYk4TAu2uv2Px+l8guMajogeuiw== - dependencies: - "@types/unist" "^2.0.0" - unist-util-is "^5.0.0" - -unist-util-visit@^4.0.0: - version "4.1.1" - resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-4.1.1.tgz#1c4842d70bd3df6cc545276f5164f933390a9aad" - integrity sha512-n9KN3WV9k4h1DxYR1LoajgN93wpEi/7ZplVe02IoB4gH5ctI1AaF2670BLHQYbwj+pY83gFtyeySFiyMHJklrg== - dependencies: - "@types/unist" "^2.0.0" - unist-util-is "^5.0.0" - unist-util-visit-parents "^5.1.1" - -universalify@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" - integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== - -update-browserslist-db@^1.0.5: - version "1.0.5" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.5.tgz#be06a5eedd62f107b7c19eb5bcefb194411abf38" - integrity sha512-dteFFpCyvuDdr9S/ff1ISkKt/9YZxKjI9WlRR99c180GaztJtRa/fn18FdxGVKVsnPY7/a/FDN68mcvUmP4U7Q== - dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" - -update-browserslist-db@^1.0.9: - version "1.0.9" - resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz#2924d3927367a38d5c555413a7ce138fc95fcb18" - integrity sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg== - dependencies: - escalade "^3.1.1" - picocolors "^1.0.0" - -uri-js@^4.2.2: - version "4.4.1" - resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" - integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== - dependencies: - punycode "^2.1.0" - -use-sync-external-store@1.2.0, use-sync-external-store@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" - integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== - -utf-8-validate@^5.0.2: - version "5.0.9" - resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.9.tgz#ba16a822fbeedff1a58918f2a6a6b36387493ea3" - integrity sha512-Yek7dAy0v3Kl0orwMlvi7TPtiCNrdfHNd7Gcc/pLq4BLXqfAmd0J7OWMizUQnTTJsyjKn02mU7anqwfmUP4J8Q== - dependencies: - node-gyp-build "^4.3.0" - -util-deprecate@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== - -uvu@^0.5.0: - version "0.5.6" - resolved "https://registry.yarnpkg.com/uvu/-/uvu-0.5.6.tgz#2754ca20bcb0bb59b64e9985e84d2e81058502df" - integrity sha512-+g8ENReyr8YsOc6fv/NVJs2vFdHBnBNdfE49rshrTzDWOlUx4Gq7KOS2GD8eqhy2j+Ejq29+SbKH8yjkAqXqoA== - dependencies: - dequal "^2.0.0" - diff "^5.0.0" - kleur "^4.0.3" - sade "^1.7.3" - -v8-to-istanbul@^9.0.1: - version "9.0.1" - resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz#b6f994b0b5d4ef255e17a0d17dc444a9f5132fa4" - integrity sha512-74Y4LqY74kLE6IFyIjPtkSTWzUZmj8tdHT9Ii/26dvQ6K9Dl2NbEfj0XgU2sHCtKgt5VupqhlO/5aWuqS+IY1w== - dependencies: - "@jridgewell/trace-mapping" "^0.3.12" - "@types/istanbul-lib-coverage" "^2.0.1" - convert-source-map "^1.6.0" - -vfile-location@^4.0.0: - version "4.0.1" - resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-4.0.1.tgz#06f2b9244a3565bef91f099359486a08b10d3a95" - integrity sha512-JDxPlTbZrZCQXogGheBHjbRWjESSPEak770XwWPfw5mTc1v1nWGLB/apzZxsx8a0SJVfF8HK8ql8RD308vXRUw== - dependencies: - "@types/unist" "^2.0.0" - vfile "^5.0.0" - -vfile-matter@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/vfile-matter/-/vfile-matter-3.0.1.tgz#85e26088e43aa85c04d42ffa3693635fa2bc5624" - integrity sha512-CAAIDwnh6ZdtrqAuxdElUqQRQDQgbbIrYtDYI8gCjXS1qQ+1XdLoK8FIZWxJwn0/I+BkSSZpar3SOgjemQz4fg== - dependencies: - "@types/js-yaml" "^4.0.0" - is-buffer "^2.0.0" - js-yaml "^4.0.0" - -vfile-message@^3.0.0: - version "3.1.2" - resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-3.1.2.tgz#a2908f64d9e557315ec9d7ea3a910f658ac05f7d" - integrity sha512-QjSNP6Yxzyycd4SVOtmKKyTsSvClqBPJcd00Z0zuPj3hOIjg0rUPG6DbFGPvUKRgYyaIWLPKpuEclcuvb3H8qA== - dependencies: - "@types/unist" "^2.0.0" - unist-util-stringify-position "^3.0.0" - -vfile@^5.0.0, vfile@^5.3.0: - version "5.3.5" - resolved "https://registry.yarnpkg.com/vfile/-/vfile-5.3.5.tgz#ec2e206b1414f561c85b7972bb1eeda8ab47ee61" - integrity sha512-U1ho2ga33eZ8y8pkbQLH54uKqGhFJ6GYIHnnG5AhRpAh3OWjkrRHKa/KogbmQn8We+c0KVV3rTOgR9V/WowbXQ== - dependencies: - "@types/unist" "^2.0.0" - is-buffer "^2.0.0" - unist-util-stringify-position "^3.0.0" - vfile-message "^3.0.0" - -w3c-hr-time@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz#0a89cdf5cc15822df9c360543676963e0cc308cd" - integrity sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ== - dependencies: - browser-process-hrtime "^1.0.0" - -w3c-xmlserializer@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/w3c-xmlserializer/-/w3c-xmlserializer-3.0.0.tgz#06cdc3eefb7e4d0b20a560a5a3aeb0d2d9a65923" - integrity sha512-3WFqGEgSXIyGhOmAFtlicJNMjEps8b1MG31NCA0/vOF9+nKMUW1ckhi9cnNHmf88Rzw5V+dwIwsm2C7X8k9aQg== - dependencies: - xml-name-validator "^4.0.0" - -walker@^1.0.8: - version "1.0.8" - resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f" - integrity sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ== - dependencies: - makeerror "1.0.12" - -webidl-conversions@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871" - integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ== - -webidl-conversions@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-7.0.0.tgz#256b4e1882be7debbf01d05f0aa2039778ea080a" - integrity sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g== - -websocket@^1.0.34: - version "1.0.34" - resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111" - integrity sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ== - dependencies: - bufferutil "^4.0.1" - debug "^2.2.0" - es5-ext "^0.10.50" - typedarray-to-buffer "^3.1.5" - utf-8-validate "^5.0.2" - yaeti "^0.0.6" - -whatwg-encoding@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53" - integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg== - dependencies: - iconv-lite "0.6.3" - -whatwg-mimetype@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz#5fa1a7623867ff1af6ca3dc72ad6b8a4208beba7" - integrity sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q== - -whatwg-url@^11.0.0: - version "11.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-11.0.0.tgz#0a849eebb5faf2119b901bb76fd795c2848d4018" - integrity sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ== - dependencies: - tr46 "^3.0.0" - webidl-conversions "^7.0.0" - -whatwg-url@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d" - integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw== - dependencies: - tr46 "~0.0.3" - webidl-conversions "^3.0.0" - -which-boxed-primitive@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" - integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== - dependencies: - is-bigint "^1.0.1" - is-boolean-object "^1.1.0" - is-number-object "^1.0.4" - is-string "^1.0.5" - is-symbol "^1.0.3" - -which@^2.0.1: - version "2.0.2" - resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" - integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== - dependencies: - isexe "^2.0.0" - -word-wrap@^1.2.3, word-wrap@~1.2.3: - version "1.2.3" - resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c" - integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ== - -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== - -write-file-atomic@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/write-file-atomic/-/write-file-atomic-4.0.1.tgz#9faa33a964c1c85ff6f849b80b42a88c2c537c8f" - integrity sha512-nSKUxgAbyioruk6hU87QzVbY279oYT6uiwgDoujth2ju4mJ+TZau7SQBhtbTmUyuNYTuXnSyRn66FV0+eCgcrQ== - dependencies: - imurmurhash "^0.1.4" - signal-exit "^3.0.7" - -ws@^8.8.0: - version "8.8.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0" - integrity sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA== - -xml-name-validator@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/xml-name-validator/-/xml-name-validator-4.0.0.tgz#79a006e2e63149a8600f15430f0a4725d1524835" - integrity sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw== - -xmlchars@^2.2.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" - integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== - -xtend@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" - integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== - -y18n@^5.0.5: - version "5.0.8" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" - integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== - -yaeti@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" - integrity sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug== - -yallist@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72" - integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A== - -yaml@^1.10.2: - version "1.10.2" - resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b" - integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg== - -yargs-parser@^21.0.0, yargs-parser@^21.0.1: - version "21.1.1" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" - integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== - -yargs@^17.3.1: - version "17.5.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.5.1.tgz#e109900cab6fcb7fd44b1d8249166feb0b36e58e" - integrity sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" - y18n "^5.0.5" - yargs-parser "^21.0.0" - -yocto-queue@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" - integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== - -yoga-layout-prebuilt@^1.10.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/yoga-layout-prebuilt/-/yoga-layout-prebuilt-1.10.0.tgz#2936fbaf4b3628ee0b3e3b1df44936d6c146faa6" - integrity sha512-YnOmtSbv4MTf7RGJMK0FvZ+KD8OEe/J5BNnR0GHhD8J/XcG/Qvxgszm0Un6FTHWW4uHlTgP0IztiXQnGyIR45g== - dependencies: - "@types/yoga-layout" "1.9.2" - -yoga-wasm-web@0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/yoga-wasm-web/-/yoga-wasm-web-0.1.2.tgz#05d3fc9cbdfd57ac9682debb5001aca075489a39" - integrity sha512-8SkgawHcA0RUbMrnhxbaQkZDBi8rMed8pQHixkFF9w32zGhAwZ9/cOHWlpYfr6RCx42Yp3siV45/jPEkJxsk6w== - -zwitch@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.2.tgz#91f8d0e901ffa3d66599756dde7f57b17c95dce1" - integrity sha512-JZxotl7SxAJH0j7dN4pxsTV6ZLXoLdGME+PsjkL/DaBrVryK9kTGq06GfKrwcSOqypP+fdXGoCHE36b99fWVoA==